Hi Sam,
Thanks a lot for nice tutorial.
But, there you have used the modified Attach.wsdl. My question is: Is it a
possible to use wsdl that it is generated from the AXIS sample
urn:EchoAttachmentService directly? If yes, please give me a hand.
I get it from deployed EchoAttachmentService
http://localhost:8080/axis/services/urn:EchoAttachmentsService?wsdl
I need to have the working sample without providing any WSDL files to the
client.
Such wsdl looks like:
//--------------------------------------------------------------------------
--------------------------
<?xml version="1.0" encoding="UTF-8" ?>
- <wsdl:definitions
targetNamespace="http://localhost:8080/axis/services/urn:EchoAttachmentsServ
ice"
xmlns:impl="http://localhost:8080/axis/services/urn:EchoAttachmentsService"
xmlns:intf="http://localhost:8080/axis/services/urn:EchoAttachmentsService"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
- <wsdl:types>
- <schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://localhost:8080/axis/services/urn:EchoAttachmentsServ
ice">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/" />
- <complexType name="ArrayOf_apachesoap_DataHandler">
- <complexContent>
- <restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType"
wsdl:arrayType="apachesoap:DataHandler[]" />
</restriction>
</complexContent>
</complexType>
</schema>
</wsdl:types>
- <wsdl:message name="echoRequest">
<wsdl:part name="dh" type="apachesoap:DataHandler" />
</wsdl:message>
- <wsdl:message name="echoDirRequest">
<wsdl:part name="attachments" type="impl:ArrayOf_apachesoap_DataHandler"
/>
</wsdl:message>
- <wsdl:message name="echoDirResponse">
<wsdl:part name="echoDirReturn" type="impl:ArrayOf_apachesoap_DataHandler"
/>
</wsdl:message>
- <wsdl:message name="echoResponse">
<wsdl:part name="returnqname" type="apachesoap:DataHandler" />
</wsdl:message>
- <wsdl:portType name="EchoAttachmentsService">
- <wsdl:operation name="echo" parameterOrder="dh">
<wsdl:input name="echoRequest" message="impl:echoRequest" />
<wsdl:output name="echoResponse" message="impl:echoResponse" />
</wsdl:operation>
- <wsdl:operation name="echoDir" parameterOrder="attachments">
<wsdl:input name="echoDirRequest" message="impl:echoDirRequest" />
<wsdl:output name="echoDirResponse" message="impl:echoDirResponse" />
</wsdl:operation>
</wsdl:portType>
- <wsdl:binding name="urn:EchoAttachmentsServiceSoapBinding"
type="impl:EchoAttachmentsService">
<wsdlsoap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
- <wsdl:operation name="echo">
<wsdlsoap:operation soapAction="" />
- <wsdl:input name="echoRequest">
<wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://localhost:8080/axis/services/urn:EchoAttachmentsService"
/>
</wsdl:input>
- <wsdl:output name="echoResponse">
<wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://localhost:8080/axis/services/urn:EchoAttachmentsService"
/>
</wsdl:output>
</wsdl:operation>
- <wsdl:operation name="echoDir">
<wsdlsoap:operation soapAction="" />
- <wsdl:input name="echoDirRequest">
<wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://attachments.samples" />
</wsdl:input>
- <wsdl:output name="echoDirResponse">
<wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://localhost:8080/axis/services/urn:EchoAttachmentsService"
/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
- <wsdl:service name="EchoAttachmentsServiceService">
- <wsdl:port name="urn:EchoAttachmentsService"
binding="impl:urn:EchoAttachmentsServiceSoapBinding">
<wsdlsoap:address
location="http://localhost:8080/axis/services/urn:EchoAttachmentsService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
//--------------------------------------------------------------------------
--------------------------
As you can see there is no string in soapAction attribute (element
operation).
So, how your low-level client can looks like to?
I just cite some important lines:
Connector.Property("EndPointURL") =
"http://localhost:8080/axis/services/urn:EchoAttachmentsService"
Connector.Property("SoapAction") = "what the name??? in my wsdl I don't have
any."
' Get the attachment.
Set Node = Reader.RpcParameter("returnqname") is it right name according
the wsdl above???
As well, I tried to use high-level API.
The problem is that
mSoapClient.MSSoapInit
"http://localhost:8080/axis/services/urn:EchoAttachmentsService"
causes the error:
SoapMapper:The schema definition with a targetnamespace of
http://xml.apache.org/xml-soap for SoapMapper DataHandler could not be found
HRESULT=0x80004005:
- SoapMapper:The SoapMapper for element DataHandler could not be created
HRESULT=0x80004005:.
- WSDLOperation:Initialization of a SoapMapper for operation echo failed
HRESULT=0x80004005:
- WSDLOperation:Initializing of the input message failed for operation echo
HRESULT=0x80004005:
- WSDLPort:An operation for port urn:EchoAttachmentsService could not be
initialized HRESULT=0x80004005:
- WSDLPort:Analyzing the binding information for port
urn:EchoAttachmentsService failed HRESULT=0x80004005:
- WSDLService:Initialization of the port for service
EchoAttachmentsServiceService failed HRESULT=0x80004005:
- WSDLReader:Analyzing the WSDL file failed HRESULT=0x80004005:
- Client:One of the parameters supplied is invalid. HRESULT=0x80070057:
Ups..., SoapMapper does know nothing about DataHandler.
Is any solution?
Thank you in advance.
Jacek Gebala
empolis Poland
****************************************************************************
******
There seems to be a lot of confusion regarding SOAP and attachments. Since
I've done a lot of research lately on the topic, I thought I'd post some
information that may be useful to others.
My goal was to write AXIS services that could accept/send attachments
from/to SOAP clients written using the "SOAP Toolkit 3.0" and the "Web
Services Enhancements 1.0" (sometimes called the WSDK) (both from
Microsoft).
Here I'll roughly describe what I went through to achieve this goal.
-------------------------------
The "Web Services Enhancements 1.0" was easiest. The sample program in...
<WSE-install>\Samples\QuickStart\Clients\Attachments
shows you how to send an "unreferenced" attachment. This is an attachment
that is *not* referenced by the soap envelope.
The first thing needed is to add the following code to their
EchoAttachment.Echo() method before the "this.invoke()" line.
----------------------------------------------------
// Needed to not confuse Apache AXIS.
this.RequestSoapContext.Path.MustUnderstand = false;
----------------------------------------------------
Without this, a WS-Routing SOAP header with a MustUnderstand value of true
will cause AXIS to generate a fault (since it doesn't understand it).
In order to specify the URL of your choice, in AttachmentsClient.Run()
method, right after...
EchoAttachment serviceProxy = new EchoAttachment();
add a line like...
----------------------------------------------------
serviceProxy.Url = "http://localhost:8080/axis/services/YourServicePath";
----------------------------------------------------
This sample shows how to send an attachment but not how to receive one. To
receive an unreferenced attachment, after the method call (in
AttachmentsClient.cs), you can add lines like...
----------------------------------------------------
if (serviceProxy.ResponseSoapContext.Attachments.Count >= 1)
{
attachment = serviceProxy.ResponseSoapContext.Attachments[0];
...
----------------------------------------------------
So now we should have a C# SOAP client that sends/accepts a DIME attachment
and won't confuse AXIS (with its special WS-Routing SOAP header).
(I used Visual Studio 7.0 to build this project)
We now need to write an AXIS service that can receive/send unreferenced
attachments. Luckily, this is easy...
In your service, you can use the current MessageContext to iterate over any
attachments that were sent to you and also to add any attachments you want
in your response.
Here is some sample code that looks for the first received attachment and
sends it back to the client.
----------------------------------------------------
org.apache.axis.MessageContext context =
org.apache.axis.MessageContext.getCurrentContext();
java.util.Iterator i = context.getMessage().getAttachments();
if (i != null)
{
if (i.hasNext())
{
Object attachmentPartCandidate = i.next();
if (attachmentPartCandidate instanceof
org.apache.axis.attachments.AttachmentPart)
{
org.apache.axis.attachments.AttachmentPart attachmentPart =
(org.apache.axis.attachments.AttachmentPart) attachmentPartCandidate;
try
{
javax.activation.DataHandler dh = attachmentPart.getDataHandler();
// Do what you will with dh!
// Build up an AttachmentPart to send in the response. For this
// sample, we'll just re-use the one we were sent.
org.apache.axis.Message responseMessage = context.getResponseMessage();
responseMessage.addAttachmentPart(attachmentPart);
}
catch (javax.xml.soap.SOAPException e)
{
// Ignore
}
}
}
}
----------------------------------------------------
----------------------------------------------------
----------------------------------------------------
Now we'll move on to MS SOAP Toolkit 3.0. This toolkit has a lot of
machinery. There are both high and low-level APIs for dealing with your
SOAP message and attachments.
The low-level attachment client examples are in...
<MSSOAP3.0>\Samples30\Attach\Client\SrSz\WshVbs
These programs are scripts (wscript, vbscript, etc..) that build up the
SOAP message directly. They don't require a WSDL.
I hacked on one of them to produce the following script which works with
the AXIS sample urn:EchoAttachmentsService (after an AXIS source code
change; more on this later).
[Start of samtest.vbs]
----------------------------------------------------------------------------
--
' I combined parts of GetPicture.vbs and PutPicture.vbs along with some
' code of my own.
Option Explicit
' This is the URL to which messages will be sent.
'Const END_POINT_URL =
"http://MSSoapSampleServer/MSSoapSamples30/Attach/Service/SrSz/AspVbs/PutPic
ture.asp"
Const END_POINT_URL =
"http://sbrow1d:9876/axis/services/urn:EchoAttachmentsService"
Dim Connector
Dim Reader
Dim Parser
Dim Serializer
Dim Attachment
Dim ReceivedAttach
Dim RcvdAttachments
Dim Node
Dim NodeList
Dim ScriptPath
Dim Composer
Dim FileAttachment
Dim res
' Create an HTTP connector and set the end point URL.
Set Connector = CreateObject("MSSOAP.HttpConnector30")
Connector.Property("EndPointURL") = END_POINT_URL
Connector.Property("SoapAction") =
"http://tempuri.org/Attach/action/Attach.echo"
' Create a file attachment object and set it's FileName property.
Set FileAttachment = CreateObject("MSSOAP.FileAttachment30")
ScriptPath =
"D:\\VS.NET\\MSSOAP3.0\\Samples30\\Attach\\Client\\SrSz\\WshVbs\\"
FileAttachment.FileName = ScriptPath & "SamplePicture.jpg"
' Create DIME composer and serializer objects, then initialize the
' serializer with the composer.
Set Composer = CreateObject("MSSOAP.DimeComposer30")
Set Serializer = CreateObject("MSSOAP.SoapSerializer30")
Serializer.InitWithComposer Connector.InputStream, Composer
' Write out the SOAP Envelope and Body start tags.
Serializer.StartEnvelope
Serializer.StartBody
' Start the method element.
Serializer.StartElement "echo"
' Write out the element that references the picture attachment.
' AXIS doesn't seem to care what this element name is.
Serializer.StartElement "Picture"
Serializer.AddAttachmentAndReference FileAttachment
Serializer.EndElement
Serializer.EndElement
' Write out the SOAP Envelope and Body end tags.
Serializer.EndBody
Serializer.EndEnvelope
' Tell the serializer that we are finished providing all message
' content (both the SOAP envelope and all attachments).
Serializer.Finished
'-----------------------------------------------------------
' Create a DimeParser30 object. This object will find the SOAP Envelope
' and any DIME attachments in the request message and make them available
' though the SoapReader30 object.
Set Parser = CreateObject("MSSOAP.DimeParser30")
' Create a SoapReader30 object. This object will provide access to the
' SOAP Evelope contents and any attachments identified by the DimeParser30
' object.
Set Reader = CreateObject("MSSOAP.SoapReader30")
res = Reader.LoadWithParser(Connector.OutputStream, Parser)
Set Node = Reader.RPCParameter("returnqname")
Set Attachment = Reader.GetReferencedAttachment(Node)
' Save the attachment to a file. Indicate that any existing file
' with this can can be replaced.
ScriptPath =
"D:\\VS.NET\\MSSOAP3.0\\Samples30\\Attach\\Client\\SrSz\\WshVbs\\"
Attachment.SaveToFile ScriptPath & "ReceivedPicture.jpg", True
----------------------------------------------------------------------------
--
[End of samtest.vbs]
The tricky part here was extracting the attachment from the response.
As I mentioned, above, getting this to work required changing one line of
AXIS source code so let me explain this.
The referenced attachment that AXIS sends out has an "href" attribute in
the body that looks something like...
cid:987623414253512151413145151
The actual DIME attachment has an ID that does *not* have the "cid:"
prefix. It appears that the MS SOAP Toolkit 3.0 is confused by this since
it is expecting a literal match.
In order to get this to work, I had to change...
org\apache\axis\attachments\AttachmentPart.java
as follows...
public String getContentIdRef() {
// Original version.
//return Attachments.CIDprefix + getContentId();
// The below version allows MSSOAP Tookit 3.0 to work. They get
confused because
// the href attribute has something like "cid:1234...." but the
attachment itself
// just has 1234... *without* the cid: prefix.
return getContentId();
}
I compiled just this file and placed its .class file in...
<axis-install>\webapps\axis\WEB-INF\classes\org\apache\axis\attachments
After restarting Tomcat, this altered .class file kicked in and produced
responses without the "cid:" prefix.
With this in place, the samtest.vbs script above worked correctly.
Microsoft seems to use a prefix of "uuid:" but they use it both in the href
attribute and in the attachment id whereas
AXIS uses "cid:" in the href attribute but not in the attachment id.
I'll now move on to the high-level API. The high-level attachment example
is in...
<MSSOAP3.0>\Samples30\Attach\Client\Rpc\Cpp
The high-level API dynamically reads a WSDL file and exposes (via COM)
interfaces for the SOAP methods described in the WSDL file.
This is kind of clunky since you have to write a lot of code to make the
call.
I started by modifying the Attach.wsdl file in this sample to have some new
entries.
[Start of modified Attach.wsdl]
-------------------------------------------------------
<?xml version='1.0' encoding='UTF-8' ?>
<!-- Generated 04/22/02 by Microsoft SOAP Toolkit WSDL File Generator,
Version 3.00.1122.0 -->
<definitions
name='Attach'
targetNamespace='http://tempuri.org/Attach/wsdl/'
xmlns:wsdlns='http://tempuri.org/Attach/wsdl/'
xmlns:typens='http://tempuri.org/Attach/type/'
xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
xmlns:stk='http://schemas.microsoft.com/soap-toolkit/wsdl-extension'
xmlns:dime='http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/'
xmlns:ref='http://schemas.xmlsoap.org/ws/2002/04/reference/'
xmlns:content='http://schemas.xmlsoap.org/ws/2002/04/content-type/'
xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
xmlns='http://schemas.xmlsoap.org/wsdl/'>
<types>
<schema
targetNamespace='http://tempuri.org/Attach/type/'
xmlns='http://www.w3.org/2001/XMLSchema'
xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'
xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
elementFormDefault='qualified'>
<import namespace='http://schemas.xmlsoap.org/soap/encoding/'/>
<import namespace='http://schemas.xmlsoap.org/wsdl/'/>
<import namespace='http://schemas.xmlsoap.org/ws/2002/04/reference/'/>
<import namespace='http://schemas.xmlsoap.org/ws/2002/04/content-type/'/>
<complexType name='UnknownBinaryContent'>
<simpleContent>
<restriction base='base64Binary'>
<annotation>
<appInfo>
<!--
You may use one or more of the following elements to describe the
binary content:
<content:type value='(URI identifying type)'/>
<content:mediaType value='(MIME media type)'/>
<content:documentType value='(QName of XML document)'/>
-->
<content:mediaType value='application/octet-stream'/>
</appInfo>
</annotation>
</restriction>
</simpleContent>
</complexType>
</schema>
</types>
<message name='Attach.PutPicture'>
<part name='Picture' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.PutPictureResponse'>
</message>
<message name='Attach.echo'>
<part name='returnqname' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.echoResponse'>
<!--
The name here doesn't seem to matter as long as it isn't
returnqname because, if it is, this value appears on the
parameterOrder attribute and because it sees it in both
the input and output portions, it thinks it is an in/out
parameter and that messes things up apparently.
-->
<part name='Sam' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.GetPicture'>
</message>
<message name='Attach.GetPictureResponse'>
<part name='Result' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.PutXml'>
<part name='SampleXml' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.PutXmlResponse'>
</message>
<message name='Attach.GetXml'>
</message>
<message name='Attach.GetXmlResponse'>
<part name='Result' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.PutRecordset'>
<part name='Recordset' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.PutRecordsetResponse'>
</message>
<message name='Attach.GetRecordset'>
</message>
<message name='Attach.GetRecordsetResponse'>
<part name='Result' type='typens:UnknownBinaryContent'/>
</message>
<message name='Attach.PutMultiple'>
<part name='OtherParam' type='xsd:string'/>
</message>
<message name='Attach.PutMultipleResponse'>
</message>
<message name='Attach.GetMultiple'>
<part name='OtherParam' type='xsd:string'/>
</message>
<message name='Attach.GetMultipleResponse'>
</message>
<message name='Attach.PutGetMultiple'>
<part name='OtherParam' type='xsd:string'/>
</message>
<message name='Attach.PutGetMultipleResponse'>
</message>
<portType name='AttachSoapPort'>
<operation name='PutPicture' parameterOrder='Picture'>
<input message='wsdlns:Attach.PutPicture'/>
<output message='wsdlns:Attach.PutPictureResponse'/>
</operation>
<operation name='echo' parameterOrder='returnqname'>
<input message='wsdlns:Attach.echo'/>
<output message='wsdlns:Attach.echoResponse'/>
</operation>
<operation name='GetPicture' parameterOrder=''>
<input message='wsdlns:Attach.GetPicture'/>
<output message='wsdlns:Attach.GetPictureResponse'/>
</operation>
<operation name='PutXml' parameterOrder='SampleXml'>
<input message='wsdlns:Attach.PutXml'/>
<output message='wsdlns:Attach.PutXmlResponse'/>
</operation>
<operation name='GetXml' parameterOrder=''>
<input message='wsdlns:Attach.GetXml'/>
<output message='wsdlns:Attach.GetXmlResponse'/>
</operation>
<operation name='PutRecordset' parameterOrder='Recordset'>
<input message='wsdlns:Attach.PutRecordset'/>
<output message='wsdlns:Attach.PutRecordsetResponse'/>
</operation>
<operation name='GetRecordset' parameterOrder=''>
<input message='wsdlns:Attach.GetRecordset'/>
<output message='wsdlns:Attach.GetRecordsetResponse'/>
</operation>
<operation name='PutMultiple' parameterOrder='OtherParam'>
<input message='wsdlns:Attach.PutMultiple'/>
<output message='wsdlns:Attach.PutMultipleResponse'/>
</operation>
<operation name='GetMultiple' parameterOrder='OtherParam'>
<input message='wsdlns:Attach.GetMultiple'/>
<output message='wsdlns:Attach.GetMultipleResponse'/>
</operation>
<operation name='PutGetMultiple' parameterOrder='OtherParam'>
<input message='wsdlns:Attach.PutGetMultiple'/>
<output message='wsdlns:Attach.PutGetMultipleResponse'/>
</operation>
</portType>
<binding name='AttachSoapBinding' type='wsdlns:AttachSoapPort' >
<stk:binding preferredEncoding='UTF-8'/>
<soap:binding style='rpc'
transport='http://schemas.xmlsoap.org/soap/http'/>
<operation name='PutPicture'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.PutPicture'/>
<input>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='Picture'/>
</input>
<output>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</output>
</operation>
<operation name='echo'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.echo'/>
<input>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='returnqname'/>
</input>
<output>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='Sam'/>
</output>
</operation>
<operation name='GetPicture'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.GetPicture'/>
<input>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</input>
<output>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='Result'/>
</output>
</operation>
<operation name='PutXml'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.PutXml'/>
<input>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='SampleXml'/>
</input>
<output>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</output>
</operation>
<operation name='GetXml'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.GetXml'/>
<input>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</input>
<output>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='Result'/>
</output>
</operation>
<operation name='PutRecordset'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.PutRecordset'/>
<input>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='Recordset'/>
</input>
<output>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</output>
</operation>
<operation name='GetRecordset'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.GetRecordset'/>
<input>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</input>
<output>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/closed-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='Result'/>
</output>
</operation>
<operation name='PutMultiple'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.PutMultiple'/>
<input>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/open-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='OtherParam'/>
</input>
<output>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</output>
</operation>
<operation name='GetMultiple'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.GetMultiple'/>
<input>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='OtherParam'/>
</input>
<output>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/open-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</output>
</operation>
<operation name='PutGetMultiple'>
<soap:operation
soapAction='http://tempuri.org/Attach/action/Attach.PutGetMultiple'/>
<input>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/open-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts='OtherParam'/>
</input>
<output>
<dime:message
layout='http://schemas.xmlsoap.org/ws/2002/04/dime/open-layout'
wsdl:required='true'/>
<soap:body
use='encoded'
namespace='http://tempuri.org/Attach/message/'
encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
parts=''/>
</output>
</operation>
</binding>
<service name='Attach' >
<port name='AttachSoapPort' binding='wsdlns:AttachSoapBinding' >
<soap:address
location='http://MSSoapSampleServer/MSSoapSamples30/Attach/Service/Rpc/Isapi
Cpp/Attach.WSDL'/>
</port>
</service>
</definitions>
-------------------------------------------------------
[End of modified Attach.wsdl]
Just search for "echo" in the file above to see the new entries I
introduced to reflect the AXIS sample urn:EchoAttachmentsService.
There were a few tricky parts. See inline comments for more details.
I then modified the OnPutPicture() method in the AttachCliRpcCpp.cpp sample
file as follows...
[Start of modified OnPutPicture() method]
-------------------------------------------------------
void CAttachCliRpcCppDlg::OnPutPicture()
{
HRESULT hr;
try
{
// Display the hourglass mouse pointer.
BeginWaitCursor();
// Create a FileAttachment30 object.
IFileAttachmentPtr pFileAttachment;
hr = pFileAttachment.CreateInstance(__uuidof(FileAttachment30));
if( FAILED(hr) ) RaiseError(L"Cannot create MSSOAP.FileAttachment30
object.", hr);
// Set the file attachment object's file name property to the
// name of the file that will be sent as an attachment.
CString FileName = m_AppPath + _T("\\SamplePicture.jpg");
pFileAttachment->FileName = _bstr_t(FileName);
// Perform the PutPicture operation.
EXCEPINFO excepinfo;
DISPID dispid;
DISPPARAMS params;
const int CARGS = 1; // One argument.
_variant_t args[CARGS];
_variant_t result;
//WCHAR *pName = L"PutPicture";
WCHAR *pName = L"echo";
unsigned int uArgErr;
// Use this to specify your own SOAP URL.
m_pSoapClient->ConnectorProperty["EndPointURL"] =
_variant_t("http://sbrow1d:9876/axis/services/urn:EchoAttachmentsService");
// Get id of "PutPicture" method.
hr = m_pSoapClient->GetIDsOfNames(
IID_NULL,
&pName,
1,
LOCALE_SYSTEM_DEFAULT,
&dispid);
//if( FAILED(hr) ) RaiseError(L"Cannot get id of PutPicture.", hr);
if( FAILED(hr) ) RaiseError(L"Cannot get id of echo.", hr);
excepinfo.wCode = 0;
excepinfo.wReserved = 0;
excepinfo.bstrSource = 0;
excepinfo.bstrDescription = 0;
excepinfo.bstrHelpFile = 0;
excepinfo.dwHelpContext = 0;
excepinfo.pvReserved = 0;
excepinfo.pfnDeferredFillIn = 0;
excepinfo.scode = 0;
params.cArgs = CARGS;
params.cNamedArgs = 0;
params.rgdispidNamedArgs = 0;
params.rgvarg = args;
// File attachment object argument. The type will be set to VT_DISPATCH.
args[0] = (IDispatch*)pFileAttachment;
hr = m_pSoapClient->Invoke(
dispid, // ID of PutPicture
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
¶ms,
&result, // Expect return value.
&excepinfo,
&uArgErr);
if( FAILED(hr) ) RaiseError(L"PutPicture failed.", hr, excepinfo, params,
uArgErr);
// Return value is expected to be an IDispatch interface to an
// ReceivedAttachment30 object.
if( result.vt != VT_DISPATCH ) RaiseError(L"echo did not return an
object.");
// Get an IReceivedAttachment interface pointer for the return value.
IReceivedAttachmentPtr pReceivedAttachment;
hr = result.pdispVal->QueryInterface(&pReceivedAttachment);
if( FAILED(hr) ) RaiseError(L"echo did not return an IReceivedAttachment
interface", hr);
// Save the picture content to a file.
FileName = m_AppPath + "\\ReceivedPicture.jpg";
pReceivedAttachment->SaveToFile(_bstr_t(FileName), VARIANT_TRUE);
// Remove the hourglass mouse pointer.
EndWaitCursor();
// Display an success message.
MessageBox("Success!");
}
catch( _com_error Error )
{
// Remove the hourglass mouse pointer.
EndWaitCursor();
// Display an error message.
DisplayError(_T("Cannot put picture."), Error);
}
}
-------------------------------------------------------
[End of modified OnPutPicture() method]
Mostly the changes needed were...
- Call "echo" instead of "PutPicture"
- Specify URL of my choice.
- Extract the returned attachment (grabbed that code from the
OnGetPicture() method)
Build the project, run it, and click the Put Picture button to exercise
this code.
This worked for me but, again, only after tweaking AXIS to not put the
"cid:" prefix on outgoing attachment references.
[Wrap up]
------------------------------------------------------
I'll post a short mail to the SOAP developers list so they know about the
code change I had to make.
I'm still waiting for a tool what will take a WSDL file that uses the DIME
extensions and cranks out a SOAP client that automatically handle
attachments. AXIS could do this now. All of the infrastructure is
there. Hopefully a future release will add this support.
I hope this posting will be of help to others. I know it would've saved me
a lot of research.
Thanks
Sam Brow