SOAP Web Service Client in Borland C++ Builder 6 - soap

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

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:)

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.

Generating SOAP Requests from Java objects

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.

Zend Framework 2 SOAP server WSDL failed to load

I can't get the SOAP server working in Zend Framework 2 module. I am not completely sure, but I believe that the problem is the WSDL file. I try to create the WSDL file via Autodiscover, which is provided by the Zend Framework. Here is the error.log:
[Fri Apr 19 20:39:29 2013] [error] [client 172.23.31.109] PHP Warning: SoapServer::SoapServer(): I/O warning : failed to load external entity "http-LINK/services?wsdl" in /PATH/public_html/vendor/zendframework/zendframework/library/Zend/Soap/Server.php on line 749
[Fri Apr 19 20:39:29 2013] [error] [client 172.23.31.109] PHP Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http-LINK/services?wsdl' : failed to load external entity "http-LINK/services?wsdl"\n in /PATH/public_html/vendor/zendframework/zendframework/library/Zend/Soap/Server.php on line 749
I added an own module for this services test, this is the structure, module is called "Services":
-Services
--config
---module.config.php
--src
---Services
----API
-----1.0
------servicesAPI.php
---Controller
----ServicesController.php
--view
---services
----serivces
-Module.php
-autoload_classmap.php
This is my file "servicesAPI.php"
class servicesAPI {
/**
* This method takes a value and gives back the md5 hash of the value
*
* #param String $value
* #return String
*/
public function md5Value($value) {
return md5($value);
}
}
And this is ServicesController.php:
namespace Services\Controller;
ini_set("soap.wsdl_cache_enabled", 0);
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Soap\AutoDiscover;
use Zend\Soap\Server;
require_once __DIR__ . '/../API/1.0/servicesAPI.php';
class ServicesController extends AbstractActionController {
private $_options;
private $_URI = "http-LINK/services";
private $_WSDL_URI = "http-LINK/services?wsdl";
public function indexAction() {
if (isset($_GET['wsdl'])) {
$this->handleWSDL();
} else {
$this->handleSOAP();
}
}
private function handleWSDL() {
$autodiscover = new AutoDiscover();
$autodiscover->setClass('servicesAPI')
->setUri($this->_URI);
$autodiscover->handle();
}
private function handleSOAP() {
$soap = new Server($this->_WSDL_URI);
$soap->setClass('servicesAPI');
$soap->handle();
}
}
So when I deploy this and open http-LINK/services in the browser, it gives me the following:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>WSDL</faultcode>
<faultstring>
SOAP-ERROR: Parsing WSDL: Couldn't load from 'http-LINK/services?wsdl' : failed to load external entity "http-LINK/services?wsdl"
</faultstring>
<detail/>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
On this call also the PHP error output is written!
If I try to open the services?wsdl in browser, it shows me this (chrome and safari):
This page contains the following errors:
error on line 3 at column 1: Extra content at the end of the document
Below is a rendering of the page up to the first error.
This method takes a value and gives back the md5 hash of the value
But if I inspect the element, it looks completely ok:
<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http-LINK/services" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="servicesAPI" targetNamespace="http-LINK/services"><types><xsd:schema targetNamespace="http-LINK/services"/></types><portType name="servicesAPIPort"><operation name="md5Value"><documentation>This method takes a value and gives back the md5 hash of the value</documentation><input message="tns:md5ValueIn"/><output message="tns:md5ValueOut"/></operation></portType><binding name="servicesAPIBinding" type="tns:servicesAPIPort"><soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/><operation name="md5Value"><soap:operation soapAction="http-LINK/services#md5Value"/><input><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http-LINK/services"/></input><output><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http-LINK/services"/></output></operation></binding><service name="servicesAPIService"><port name="servicesAPIPort" binding="tns:servicesAPIBinding"><soap:address location="http-LINK/services"/></port></service><message name="md5ValueIn"><part name="value" type="xsd:string"/></message><message name="md5ValueOut"><part name="return" type="xsd:string"/></message></definitions>
I can validate this xml with any xml validator, it seems to be valid.
I read all the posts concerning this on stackoverflow, searched google, but none of the solutions helped me. Here a short list of what else I tried:
According to this: https://bugs.php.net/bug.php?id=48216 I tried to save the wsdl xml to a file and open it from this file when starting the soap server, fail
I tried to run the autodiscover and soapserver statements with try/catch to catch any exceptions, nothing appears
I tried with echo-ing through toXML() and other outputs, fail
Used XMLReader::open and isValid to make sure, that the xml is valid (it is)
Some more information:
PHP Version 5.3.23
Ubuntu server 11.04
php-soap module is loaded
Zend Framework version 2.1.4
Any help or hints are appreciated. Thanks in advance.
Try instantiate the Soap Server class this way:
...
private function handleSOAP() {
$soap = new Server(
null, array(,
'wsdl' => http-LINK/services?wsdl,
)
);
$soap->setClass('servicesAPI');
$soap->handle();
}
....
Also you should add this line to the end of your indexAction()
return $this->getResponse();
.. it disables the layout.

MULE ESB : Binding multiple Web Services to one Client

I have a client which connects to a Web service to get some information. I have a requirement where I have to send the same information to multiple services using different ports. To solve this without modifying the client code I found MULE ESB, which is supposed to do exactly what I need.
I've found a guide where I could connect one client to one service using MULE ESB and one port, but I cant find a way to chain the services so they all listen to one port but have different themselves.
This is how it's supposed to look like:
UPDATE :
here is my current Mule Applications config :
<mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="CE-3.2.1" xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd ">
<flow name="flows1Flow1" doc:name="flows1Flow1">
<http:inbound-endpoint exchange-pattern="request-response" address="http://localhost:4433/miniwebservice" mimeType="text/xml" doc:name="HTTP"/>
<http:outbound-endpoint exchange-pattern="request-response" address="http://localhost:4434/miniwebservice?wsdl" mimeType="text/xml" doc:name="HTTP"/>
</flow>
</mule>
Here is the WebService :
Client :
package miniwebservice;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class TestWsClient
{
public static void main( final String[] args ) throws Throwable
{
String url = ( args.length > 0 ) ? args[0] : "http://localhost:4434/miniwebservice";
Service service = Service.create(
new URL( url + "?wsdl" ),
new QName( "http://miniwebservice/", "HalloWeltImplService" ) );
HalloWelt halloWelt = service.getPort( HalloWelt.class );
System.out.println( "\n" + halloWelt.hallo( args.length > 1 ? args[1] : "" ) );
}
}
Server :
package miniwebservice;
import javax.xml.ws.Endpoint;
public class TestWsServer
{
public static void main( final String[] args )
{
String url = ( args.length > 0 ) ? args[0] : "http://localhost:4434/miniwebservice";
Endpoint.publish( url, new HalloWeltImpl() );
}
}
InterfaceImpl :
package miniwebservice;
import javax.jws.WebService;
#WebService( endpointInterface="miniwebservice.HalloWelt" )
public class HalloWeltImpl implements HalloWelt
{
public String hallo( String wer )
{
return "Hallo " + wer;
}
}
interface :
package miniwebservice;
import javax.jws.*;
#WebService
public interface HalloWelt
{
public String hallo( #WebParam( name = "wer" ) String wer );
}
If I start the Server and the Mule aplication and try to reach http://localhost:4434/miniwebservice?wsdl ower http://localhost:4433/miniwebservice I get the folowing Exception in my Browser (FireFox 8.0) :
Couldn't create SOAP message due to exception: XML reader error: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,1]
Message: Content is not allowed in prolog.
I just started to work with Mule so I thouth that this would be enouth to get redirect mule to the Service to get the wsdl but its seems like its a bit complicatet.
Disclaimers:
This is not the final solution to the whole problem, which includes dispatching to several services and aggregating results, but a step in the right direction.
This is not representative of how web service proxying is done in Mule (which is way simpler) but a barebone approach to HTTP request routing so aggregation can be added.
Since you want to forward HTTP GET requests to the ?wsdl processor and HTTP POST SOAP request to the web service, you need to handle the target HTTP method and request URI propagation yourself:
<flow name="flows1Flow1">
<http:inbound-endpoint exchange-pattern="request-response"
address="http://localhost:4433/miniwebservice" />
<message-properties-transformer scope="outbound">
<add-message-property key="http.method" value="#[header:INBOUND:http.method]" />
</message-properties-transformer>
<logger level="INFO" category="ddo" />
<http:outbound-endpoint exchange-pattern="request-response"
address="http://localhost:4434#[header:INBOUND:http.request]" />
</flow>
(tested and validated with TestWsClient and TestWsServer)