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>
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;
}
}
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.