We have a Delphi web service client that has been in production for
several years. We recently deployed a version built with Delphi 2007
(December 2007 update) and our users now report that error messages from
custom web service exceptions are no longer being displayed. We have
reproduced the problem in a simple client and server described below.
The server is a Java (Apache Axis) web application, with the following
interface:
package com.bigcreek.test;
public interface HelloService {
public String sayHello() throws HelloException;
}
HelloException is a wrapper for error messages from a legacy system, and
has three properties:
int code
String message
String originalMessage
The WSDL for the web service is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://test.bigcreek.com"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://test.bigcreek.com"
xmlns:intf="http://test.bigcreek.com"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--WSDL created by Apache Axis version: 1.4
Built on Apr 22, 2006 (06:55:48 PDT)-->
<wsdl:types>
<schema targetNamespace="http://test.bigcreek.com"
xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="HelloException">
<sequence>
<element name="code" type="xsd:int"/>
<element name="message" nillable="true" type="soapenc:string"/>
<element name="originalMessage" nillable="true"
type="soapenc:string"/>
</sequence>
</complexType>
</schema>
</wsdl:types>
<wsdl:message name="HelloException">
<wsdl:part name="fault" type="impl:HelloException"/>
</wsdl:message>
<wsdl:message name="sayHelloResponse">
<wsdl:part name="sayHelloReturn" type="soapenc:string"/>
</wsdl:message>
<wsdl:message name="sayHelloRequest">
</wsdl:message>
<wsdl:portType name="HelloService">
<wsdl:operation name="sayHello">
<wsdl:input message="impl:sayHelloRequest"
name="sayHelloRequest"/>
<wsdl:output message="impl:sayHelloResponse"
name="sayHelloResponse"/>
<wsdl:fault message="impl:HelloException" name="HelloException"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HelloServiceSoapBinding" type="impl:HelloService">
<wsdlsoap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="sayHello">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="sayHelloRequest">
<wsdlsoap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://test.bigcreek.com" use="encoded"/>
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<wsdlsoap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://test.bigcreek.com" use="encoded"/>
</wsdl:output>
<wsdl:fault name="HelloException">
<wsdlsoap:fault
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
name="HelloException" namespace="http://test.bigcreek.com" use="encoded"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloServiceService">
<wsdl:port binding="impl:HelloServiceSoapBinding"
name="HelloService">
<wsdlsoap:address
location="http://localhost:8080/HelloException/services/HelloService"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
The Delphi client code is as follows:
procedure TForm1.Button1Click(Sender: TObject);
var
service: HelloService;
begin
try
service := GetHelloService;
service.sayHello;
except
on E: HelloException do ShowMessage('HelloException: ' +
E.originalMessage);
on E: ERemotableException do ShowMessage('ERemotableException: ' +
E.Message);
on E: Exception do ShowMessage('Oridinary Exception: ' + E.Message);
end;
end;
When calling the web service, the Delphi client sends the following:
<?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"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<NS1:sayHello xmlns:NS1="http://test.bigcreek.com"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
and the web service responds (by design it always throws a
HelloException) with this:
<?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>
<soapenv:Fault>
<faultcode>soapenv:Server.userException</faultcode>
<faultstring>com.bigcreek.test.HelloException: Hello
exception</faultstring>
<detail>
<ns1:fault xmlns:ns1="http://test.bigcreek.com"
xsi:type="ns1:HelloException">
<code xsi:type="xsd:int">99</code>
<message
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="soapenc:string">Hello exception</message>
<originalMessage
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="soapenc:string">I always throw an exception</originalMessage>
</ns1:fault>
<ns2:hostname
xmlns:ns2="http://xml.apache.org/axis/">bcs137.bigcreek.com</ns2:hostname>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
So far, everything appears completely normal. However, the client
receives an ERemotableException instead of the expected HelloException
and we are therefore unable to access the wrapped exception information.
This used to work correctly in Delphi 2005 and Delphi 2006. We're not
sure about Delphi 2007 prior to the December 2007 update.
Is anyone else experiencing this problem, and does anyone have a solution?
--David W. Body / Big Creek Software, LLC
Thank you for the post. I was not aware of this issue but I do know that
some changes were made in the logic to map a fault node to an exception
type. More specifically, we use to rely strictly on the xsi:type="xxxx"
attribute (partly because that's what Delphi WebServices generate) but some
SOAP implementations don't use xsi:type. There's a big comment about the
issues we ran into in the file OPtoSOAPDomConv.pas:
Here's an excerpt from around line 1388:
{ If there's a <detail> node, try to map it to a registered type }
if FD <> nil then
begin
{ Some SOAP stacks, including Delphi6 and others (see
http://softwaredev.earthweb.com/script/article/0,,12063_641361_2,00.html)
use the approach of putting custom fault info right at the <detail>
node:
Listing 4 - Application Fault Details
<SOAP-ENV:Fault>
<faultcode>300</faultcode>
<faultstring>Invalid Request</faultstring>
<runcode>1</runcode>
<detail xmlns:e="GetTemperatureErr-URI"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xsi:type="e:GetTemperatureFault">
<number>5575910</number>
<description>Sensor Failure</description>
<file>GetTemperatureClass.cpp</file>
<line>481</line>
</detail>
</SOAP-ENV:Fault>
However, much more common is the approach where the type and namespace
are on the childnode of the <detail> node. Apache, MS and the SOAP
spec.
seem to lean towards that approach:
Example 10 from the SOAP 1.1 Spec:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Server Error</faultstring>
<detail>
<e:myfaultdetails xmlns:e="Some-URI">
<message>
My application didn't work
</message>
<errorcode>
1001
</errorcode>
</e:myfaultdetails>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
For interop reasons we favor the later approach but we'll support both
here!!
}
[End of excerpt]
Our goal (as per the note) is to support both approaches. And this approach
is not new to D2007. However, in D2007 a change went in the
TOSPADomConv.GetElementType routine to handle some cases where we were
failing before.
I don't know if you're in the position to do this but if yes, could you take
TSOAPDomConv.GetElementType from a pre D2007 version, drop it in your copy
of OpTOSOAPDomConv.pas, link with the updated runtime and let me know it
solves the problem.
We have have a VM with some Axis webservice that we use for unit testing.
I'll try to setup one that's similar to the one you mentioned to investigate
as soon as time allows.
Cheers,
Bruneau.
Bruneau,
I copied the D2007 OPToSOAPDomConv.pas file into my project folder and
added it to my project. (I also copied ComVer.inc so my project would
compile.) I then replaced the TSOAPDomConv.GetElementType function in
this unit with the version from D2006, and that appears to fix the problem.
Is there any reason we shouldn't use this in our production application
until there is an official fix?
Thanks very much for your help.
--David
Thank you for confirming that indeed the change to GetElementType was
relevant.
Later today I'll review the RAID/QC was warranted the change. I do recall
being uncomfortable changing GetElementType in that way but the report was a
valid one. I hope it's not one of these 'this way works with Axis but breaks
.NET and that way works with .NET but breaks Axis'. I've had this kind of
issue before.
More later!
Bruneau.
PS: To answer your question, no, there's no reason not to take that
approach. The ultimate/official fix will probably be different but your
approach is perfectly fine.
Just a follow-up post to mentioned that I looked up some information about
the change to GetElementType. It was made on Feb. 28th/2007 and was based on
QC report 33983. ( http://qc.codegear.com/wc/qcmain.aspx?d=33983 ). Here I
should point out that the QC report proposed a new routine rather than
modifying GetElementType but that I (we) typically avoid interface changes
as they cause problems for backward compatibility.
The test case for that report involves the case where the exception node
does not contain the 'xsi:type' attribute. Since Delphi uses xsi:type, I'll
assume that the change made still works with Delphi servers but breaks with
Axis ones (or involves issues such as cases where the exception type is in a
different namespace from the response). I need to investgate some more to
find the actual cause of failure.
I'm also noticing that our unit tests does not cover exception handling with
Axis. I'm making a note to remedy this.
In general rpc|encoded services use xsi:type. So reverting the change is
perfectly safe for your one one client. Ultimately we want the code to work
in all cases (doc|lit or rpc|encoded; with or without xsi:type attribute;
Axis, Delphi or .Net).
If time allows, would you mind opening a QC report? You only need to cut and
paste the original newgroup post and I can handle the rest. I just want a
way to track this issue. I'm making a note too but in general it's best if
the report comes from a customer. It carries more weight if I propose that a
fix be included in an upcoming patch.
Thank you for the report!
Cheers,
Bruneau.
> If time allows, would you mind opening a QC report? You only need to cut and
> paste the original newgroup post and I can handle the rest. I just want a
> way to track this issue. I'm making a note too but in general it's best if
> the report comes from a customer. It carries more weight if I propose that a
> fix be included in an upcoming patch.
>
> Thank you for the report!
>
QC #57210
You're welcome, and thanks for your help.
--David
I've asked someone to promote the QC to the internal RAID system - it's
internal report #257255.
Cheers,
Bruneau.