Web Service Development Guide : PART IV Extending your Applications : Chapter 17 SOAP with Attachments : Working with Attachments
Working with Attachments
HydraExpress represents each object defined as a MIME attachment in a WSDL document as an instance of rwsf::MessageAttachment. This class encapsulates the message payload and Content-Type information, and may also include Content-Id or Content-Location data.
To send a SOAP message with an attachment, follow these steps:
1. Define the attachment in WSDL, including the abstract definitions that describe the operation in which the attachment is sent, and the concrete definition that describes the MIME binding.
2. Generate code.
3. Implement and run your service.
This section discusses each of these steps. The discussion is based on the shipped example MIME located in your <installdir>\examples\webservices\MIME directory. This directory includes a mime.wsdl file as well as sample implementations provided so you can quickly build and test the example.
Example Service Description
The MIME example is a basic request-response client-service example, using a MIME binding. It includes two operations, AddDocument to send an explicitly-referenced MIME attachment, and GetDocuments to return a list of links to unreferenced MIME attachments (“Generate Code and Manipulate the Attachment”). The latter illustrates the use of the schema type swaRef defined by the WS-I Attachments Profile 1.0 providing an interoperable way to identify a message attachment.
Define the Attachment in WSDL
To define an attachment in WSDL, first define its abstract aspects, including its Schema type, its WSDL message parts and the operation(s) that will use it. Finally, define the concrete MIME bindings.
Define the Schema Type
First, define the object’s type either in the WSDL file’s types element, or in an associated or embedded XML Schema. Here’s an excerpt from mime.wsdl in the MIME directory. This example illustrates both traditional attachments as specified by SwA, as well as unreferenced attachments as described by WS-I Attachments Profile 1.0.
 
<types>
<xsd:schema targetNamespace="http://www.roguewave.com/rwsf/webservice/
examples/MIME.wsdl">
<!-- swaRef schema type, as defined in WS-I Attachments Profile 1.0 -->
<!-- http://www.ws-i.org/Profiles/AttachmentsProfile-1.0-2004-08-24.html -->
<xsd:simpleType name="swaRef">
<xsd:restriction base="xsd:anyURI"/>
</xsd:simpleType>
 
<xsd:complexType name="DocumentPair">
<xsd:sequence>
<xsd:element name="DocumentName" type="xsd:string"/>
<xsd:element name="DocumentRef" type="tns:swaRef"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DocumentPairList">
<xsd:sequence>
<xsd:element name="List"
type="tns:DocumentPair"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Documents" type="tns:DocumentPairList"/>
</xsd:schema>
</types>
Note the simple type swaRef. This a schema type defined by the WS-I Attachments Profile 1.0 that identifies any associated message attachment. The restriction xsd:anyURI references an attachment in the message. By using a common type, all recipients know that the referenced message is an attachment. (For more information, see http://www.ws-i.org/Profiles/AttachmentsProfile-1.0-2004-08-24.html#Referencing_Attachments_from_the_SOAP_Envelope). This type is used in unreferenced attachments (see “Generate Code and Manipulate the Attachment”).
The WSDL defines two complex types, DocumentPair and DocumentPairList, which is simply a list of elements of type DocumentPair. DocumentPair contains two elements, DocumentName, the MIME document’s name, and DocumentRef, MIME document’s key. DocumentRef is of type swaRef, which identifies it as a link to an attachment. In addition, the element Documents is declared, of type DocumentPairList.
Define the WSDL Message Parts and Operation
The mime.wsdl document defines two messages, Document and DocumentList, as follows:
 
<message name="Document">
<part name="name" type="xsd:string"/>
<part name="document" type="xsd:base64Binary"/>
</message>
<message name="DocumentList">
<part name="documents" element="tns:Documents"/>
</message>
<message name="Void"/>
...
The message Document contains two parts, including a name as an xsd:string, and a document of type xsd:base64binary. Message DocumentList contains one part, documents, which is of type DocumentPairList as defined in the above types element.
The messages Document and DocumentList are passed in two request/response operations:
 
<portType name="DocumentManagerPortType">
<operation name="AddDocument">
<input message="tns:Document"/>
<output message="tns:Void"/>
</operation>
<operation name="GetDocuments">
<input message="tns:Void"/>
<output message="tns:DocumentList"/>
</operation>
</portType>
The WSDL excerpt above defines two operations, AddDocument and GetDocuments. The operation AddDocument sends a document name and part containing the document's contents. The contents are sent as an explicitly referenced MIME attachment. The operation GetDocuments returns a MIME document that contains a list of document names and keys, along with a set of unreferenced MIME attachments. We can then look up the documents contents in the MIME attachments list via the swaRef links provided in the DocumentList.
Define the MIME Bindings
The MIME binding defines the MIME type multipart/related for the message response. Let’s look at the binding for the AddDocument and GetDocuments operations:
 
<binding name="DocumentManagerBinding" type="tns:DocumentManagerPortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="AddDocument">
<soap:operation soapAction="adddocument"/>
<input>
<mime:multipartRelated> <!-- 1 -->
<mime:part>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/examples"
</mime:part>
<mime:part>
<mime:content part="document" type="*/*"/> <!-- 2 -->
</mime:part>
</mime:multipartRelated>
</input>
<output>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/examples"
</output>
</operation>
<operation name="GetDocuments">
<soap:operation soapAction="getdocuments"/>
<input>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/examples"/>
</input>
<output>
<mime:multipartRelated>
<mime:part>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/examples"/>
</mime:part>
</mime:multipartRelated>
</output>
</operation>
</binding>
//1 Defines the binding for the input message’s multipart mime attachment, consisting of two mime parts, one the body of the message, the other the image.
//2 Defines a referenced attachment (see below), tied to the document part. Note the type indicates that any MIME type is allowed for this attachment.
Generate Code and Manipulate the Attachment
Use rwsfgen to generate code for this project. (For information on how to generate code, see “Invoking the Generator”.). If you use the provided HydraExpress project file example-project.xml, HydraExpress places the generated code into a code generation directory MIMEExample.
In the client and server sample implementations, HydraExpress generates service operation methods that take as parameters instances of rwsf::MessageAttachment.
The resulting code differs depending on whether the attachment is referenced or unreferenced:
Referenced attachments are used when WSDL bindings contain a mime:content element, naming the part to be the message attachment. Since the full part is known to be an attachment, the generated code can use rwsf::MessageAttachment to represent the part in the generated client and server implementations. The generated service operation methods use rwsf::MessageAttachment as the part's type.
Unreferenced attachments do not have a mime:content element; rather, only a soap:body element represents the actual SOAP message. In this design, the attachment is added to the message and parsed separately, but is not referenced as a SOAP message part.
Instead, another method is used to reference the attachment, the WS-I Attachments Profile 1.0 swaRef type identified above. In this case, the code generator creates simple types as the parts in the generated service operation methods. The part that includes the swaRef type will have standard accessors available to retrieve the reference link. To access the attachment, use the getResponseAttachment() method on rwsf::CallInfo with the reference link provided in the swaRef field (“Retrieving an Unreferenced Attachment”).
Sending a Referenced Attachment
Here’s the code for the method addDocument in the provided sample server implementation (located in the root directory of the MIME example), DocumentManagerPortTypeImp.cpp:
 
void
DocumentManagerPortTypeImp::addDocument(rwsf::CallInfo& callInfo,
const std::string& name_in,
const rwsf::MessageAttachment& document_in) // 1
{
documentMap_.insert(std::make_pair(name_in, document_in));
}
 
As line //1 shows, the referenced attachment generates the document parameter (labeled as document_in since it is an input parameter) as an rwsf::MessageAttachment instance. This rwsf::MessageAttachment object represents the whole part as a MIME attachment.
The method implementation simply adds the document to an internal data structure used by HydraExpress to hold state between service calls.
Retrieving an Unreferenced Attachment
Let’s look at the WSDL binding for a method getDocuments that returns a MIME attachment:
 
<binding name="DocumentManagerBinding" type="tns:DocumentManagerPortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
</binding>
Note that, unlike the addDocument example above, this binding does not list a mime:content element and does not reference a part. This is called an “unreferenced attachment”, while the example for addDocument showcases a referenced attachment where the MIME elements directly reference the part.
Here’s the code that is provided for the client operation getDocuments() in the sample client implementation DocumentManagerPortClient.cpp. Recall that the getDocuments method returns a MIME document that contains a list of document names and keys, and a set of unreferenced MIME attachments.
 
void invoke_getDocuments(DocumentManagerBindingProxy& proxy)
{
tns::Documents documents_ret; //1
rwsf::CallInfo callInfo;
 
try {
documents_ret = proxy.getDocuments(callInfo); //2
typedef tns::Documents::ListVector vector_type;
vector_type items = documents_ret.getListVector();
for (vector_type::const_iterator it = items.begin(); it != //3
items.end(); ++it) {
std::string name = (*it).getDocumentName();
std::cout << "Received document: " << name << std::endl;
std::string url = (*it).getDocumentRef(); //4
std::string uid(url);
rwsf::MessageAttachment att = //5
callInfo.getResponseAttachment(uid);
if (att.getUniqueId() == uid) { //6
std::string contents = att.getPayload();
std::cout << " contents= " << contents << std::endl;
}
else {
std::cout << " No contents were found." << std::endl;
}
}
} catch(const rwsf::SoapFaultException& e) {
std::cout << "Fault Code: " << e.getFault().getFaultcode().asString()
<< std::endl;
std::cout << "Fault String: " << e.getFault().getFaultstring() << std::endl;
}
}
//1 Note that the response is an instance of tns::Documents, which is defined in the WSDL as an instance of a tns::DocumentPairList and is therefore generated as a std::vector of tns::DocumentPair elements.
//2 Make the actual client call to the web service operation. Returns the DocumentPairList containing the documents.
//3 Begins iteration over all items in the Document list, and prints to standard out that the document has been received.
//4 Here, we extract the Content-ID for the associated attachment. Remember that the variable documentRef is of type tns:swaRef, and so represents any associated message attachment using a URI as a reference.
//5 Retrieves the message attachment as an instance of rwsf::MessageAttachment, based on the URL specified in the SOAP message. Note that the attachment is returned using rwsf::CallInfo’s method getResponseAttachment based on its Content-ID.
//6 Retrieves the attachment if it’s found.
Retrieving a Referenced Attachment
The GetDocuments method illustrates client-side working with unreferenced attachments. If the WSDL described another operation called getDocument which accessed a referenced attachment it might look like this:
 
<operation name="GetDocument">
<soap:operation soapAction="getdocument"/>
<input>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/examples"/>
</input>
<output>
<mime:multipartRelated>
<mime:part>
<soap:body use="literal"
namespace="http://www.roguewave.com/rwsf/webservice/examples"/>
</mime:part>
<mime:part>
<mime:content part="document" type="*/*"/>
</mime:part>
</mime:multipartRelated>
</output>
</operation>
This getDocument service operation would take a documentKey as input and return the full attachment in the part called document. The generated getDocument service operation method would return the part as an rwsf::MessageAttachment instance.
So we could write the invoke_getDocument() client implementation code to look like:
 
void invoke_getDocument(DocumentManagerBindingProxy& proxy)
{
std::string documentKey_in = "whatever";
rwsf::MessageAttachment document_out;
rwsf::CallInfo callInfo;
 
document_out = proxy.getDocument(callInfo, documentKey_in);
std::string contentType = document_out.getContentType();
std::string image = document_out.getPayload();
...
}
Build and Run the Example
Build and run the example, as usual. For basic information on compiling and deploying, see “Compiling the Service” and “Deploying the Service”.
Before building this example, remember to copy the provided sample files from the <installdir>\examples\webservices\MIME directory to the directory in which you generated code (called MIMEExample below), allowing the provided files to overwrite the generated files, as follows:
DocumentManagerPortTypeImp.h
Copy to MIMEExample\app\server
DocumentManagerPortTypeImp.cpp
Copy to MIMEExample\app\server
DocumentManagerPortClient.cpp
Copy to MIMEExample\app\client
Run the client from the MIMEExample\bin directory:
 
prompt> DocumentManagerPortClient.exe
Sending document: readme.txt
Sending document: project.xml
Received document: project.xml
contents= <project>This is a simple xml document.</project>
Received document: readme.txt
contents= This is a simple readme document.