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:
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.
Generate code.
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>\tutorials\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.
The MIME example is a basic request-response client-service example, using a MIME binding. It includes two operations, AddDocument to send an explicit MIME attachment, and GetDocuments that returns a list of documents that reference MIME attachments. It illustrates the use of the schema type swaRef defined by the WS-l Attachments Profile providing an interoperable way to identify a message attachment.
To define an attachment in WSDL, first define its abstract aspects, including its type, its message parts and the operation(s) that will use it. Finally, define the concrete binding.
First, define the object's type either in the WSDL'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.
<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-l Attachments Profile 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.)
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 reference to an attachment. In addition, the element Documents is declared, of type DocumentPairList.
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 explicit MIME attachment. The operation GetDocuments returns a MIME document that contains a list of document names and keys, and a set of unreferenced MIME attachments. We can then look up the documents contents in the MIME attachments list.
The MIME binding defines the MIME type multipart/related for the message response. Let's look at the binding for the AddDocument operation:
<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="encoded" namespace="http://www.roguewave.com/webservice/examples" encodingStyle="http://schemas.xmlsoap.org/soap/encoding"/> </mime:part> <mime:part> <mime:content part="document" type="*/*"/> <!-- 2 --> </mime:part> </mime:multipartRelated> </input> <output> <soap:body use="encoded" namespace="http://www.roguewave.com/webservice/examples" encodingStyle="http://schemas.xmlsoap.org/soap/encoding"/> </output> </operation> </binding>
//1 | Defines the binding for the output message's multipart mime attachment, consisting of a two mime parts, one the body of the message, the other the image. |
//2 | Note the unspecified type which indicates that any type is allowed for this attachment. |
Generate code, as always. (For information on how to generate code, see Section 7.2.2.). 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.
Let's look at the code for the method addDocument in the generated sample server implementation, DocumentManagerPortTypeImp.cpp:
#include <rwsf/webservice/Fault.h> #include <rwsf/core/NamedObject.h> #include "DocumentManagerPortTypeImp.h" RWSF_DEFINE_MESSAGE_HANDLER(DocumentManagerPortTypeImp) void //1 DocumentManagerPortTypeImp::addDocument(rwsf::CallInfo& callInfo, const std::string& name_in, const rwsf::MessageAttachment& document_in) { throw rwsf::ServerFault("Sorry: The service was invoked but the requested operation \"addDocument\" has not been implemented. An implementation must be written."); }
Line //1 is the service operation method that adds an attachment. In this case, the method adds a MIME attachment to a document list stored on the server. Note that the attachment will be sent as an explicit rwsf::MessageAttachment instance.
Now let's look at the provided sample implementation DocumentManagerPortTypeImp.cpp (located directly in the MIME directory).
void DocumentManagerPortTypeImp::addDocument(rwsf::CallInfo& callInfo, const std::string& name_in, const rwsf::MessageAttachment& document_in) { documentMap_[name_in] = document_in; }
This method simply adds the document to an internal data structure used by HydraExpress to hold state between service calls.
Let's look at the code that is generated for the service operation method 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 { //2 documents_ret = proxy.getDocuments(callInfo); } 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 | This is the generated try/catch block around the service operation method where you would implement the method. |
To retrieve the attachment, replace the try/catch block with some real work. Let's look at the provided sample client implementation.
try { documents_ret = proxy.getDocuments(callInfo); typedef tns::Documents::ListVector vector_type; vector_type items = documents_ret.getListVector(); for (vector_type::const_iterator it = items.begin(); it != //1 items.end(); ++it) { std::string name = (*it).getDocumentName(); std::cout << "Received document: " << name << std::endl; std::string url = (*it).getDocumentRef(); //2 std::string uid(url.data(), url.length()); rwsf::MessageAttachment att = //3 callInfo.getResponseAttachment(uid); if (att.getUniqueId() == uid) { //4 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 | Begins iteration over all items in the Document list, and prints to standard out that the document has been received. |
//2 | 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. |
//3 | 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. |
//4 | Retrieves the attachment if it's found. |
Accessing a Referenced Attachment:
This example illustrates client-side working with unreferenced attachments. If the WSDL described an operation getDocument to access a referenced attachment, we could write code something like:
void invoke_getDocument(DocumentManagerBindingProxy& proxy) { std::string documentKey_in; 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(); }
This service operation method takes a documentKey as input and returns an instance of rwsf::MessageAttachment, document_out.
Build and run the example, as usual. For basic information on compiling and deploying, see Section 8.2.3 and Section 8.2.4.
Before building this example, remember to copy the provided sample files from the <installdir>\tutorials\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.
©2004-2007 Copyright Quovadx, Inc. All Rights Reserved.
Quovadx and Rogue Wave are registered trademarks of Quovadx, Inc. in the United States and other countries. All other trademarks are the property of their respective owners.
Contact Rogue Wave about documentation or support issues.