Using MTOM and Apache FOP with SOAP Web Services
This blog entry builds off the WSDL-first DoubleIt tutorial to demonstrate the Message Transmission Optimization Mechanism (MTOM) for sending and receiving PDF files via
uploadPDF (client->web service provider) and
downloadPDF (WSP->client) SOAP operations. While
uploadPDF just sends a static PDF file to the WSP, the downloadPDF operation dynamically constructs a PDF containing the results of doubling the number sent in the SOAP request. The finished tutorial source code can be obtained from GitHub by using either the "Clone or Download" button or
git clone -v git://github.com/gmazza/blog-samples.git command.
Technologies highlighted in this sample:
MTOM using XML-binary Optimized Packaging (XOP) - MTOM commonly refers to the process of taking base64Binary data within a SOAP message and storing it in an optimized binary format as a MIME attachment. The MTOM Recommendation, however, mostly provides references to the XOP Recommendation where this process is actually defined. Apache CXF has an MTOM documentation page, also the IBM WebSphere App Server documentation provides another good resource.
Apache FOP - FOP is an XSL processor that takes XML documents and generates PDF documents from them (it can generate other output formats as well.) XSL is most commonly used when you want to maintain different output formats for the same source document, such as when using Docbook to output to HTML and PDF. In our example below, the web service provider will use FOP to generate the PDF.
XSLT - XSLT will be used to take the incoming integer from the SOAP request and place the doubled value of it in an XML document that has the XSL tags used by FOP to generate the PDF. Although the stylesheet I'm providing is minimal, FOP allows you to get quite detailed and artistic, and there are also commercial XSL processors such as RenderX and AntennaHouse with different and frequently better capabilities.
Please refer back to the WSDL-first tutorial for this task, as the steps below list just the changes needed in that tutorial to support PDF document transmission.
In Step #2, add the below dependencies element to the service/pom.xml file so the web service provider will have access to FOP and JavaMail. We're including the Mail API dependency because of a helpful ByteArrayDataSource class it provides.
<dependencies> <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>fop</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>javax.mail-api</artifactId> <version>1.5.6</version> </dependency> </dependencies>
Also in this step, create the XSLT file for formatting the PDF document. Call it DoubleIt.xsl and place it in the service submodule's src/main/
For Step #3, use the below DoubleIt.wsdl instead. Note the generic
application/octet-stream expectedContentType suitable for transferring PDF documents being used in the downloadPDF request and uploadPDF response which involve transferring a PDF.
For Step #6, use this Web Service Provider instead to return the PDF document.
For Step #8, replace the WSClient with the one below. It receives PDF documents (here's a sample) and saves it to the directory you ran the client from. It then uploads an already existing PDF located in its Maven resources folder.
A packet sniffer such as Wireshark can show MTOM in operation. A CXF SOAP response to a download request from the client is shown below. The PDF document is in its own MIME attachment in an optimized binary format and referenced back to the SOAP message by way of an xop:Include element that holds the PDF's MIME Content-ID value.
HTTP/1.1 200 Content-Type: multipart/related; type="application/xop+xml"; boundary="uuid:4b9b1267-c8e8-458c-aed5-eed03c5c2373"; start="<email@example.com>"; start-info="text/xml" Content-Length: 5987 Date: Sat, 13 May 2017 10:48:29 GMT --uuid:4b9b1267-c8e8-458c-aed5-eed03c5c2373 Content-Type: application/xop+xml; charset=UTF-8; type="text/xml" Content-Transfer-Encoding: binary Content-ID: <firstname.lastname@example.org> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:DownloadPDFResponse xmlns:ns2="http://www.example.org/schema/DoubleIt"> <doubledNumber> <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:email@example.com"/> </doubledNumber> </ns2:DownloadPDFResponse> </soap:Body> </soap:Envelope> --uuid:4b9b1267-c8e8-458c-aed5-eed03c5c2373 Content-Type: application/pdf Content-Transfer-Encoding: binary Content-ID: <firstname.lastname@example.org> %PDF-1.4 %.... 1 0 obj << /Creator (Apache FOP Version 2.2) /Producer (Apache FOP Version 2.2) /CreationDate (D:20170513064829-04'00') >> endobj 2 0 obj << /N 3 /Length 3 0 R /Filter /FlateDecode >> stream ...rest of PDF attachment...
The web service provider in this example takes the input number and makes a small XML document out of it prior to processing it with the DoubleIt.xsl file. As an alternative processing method, since the SOAP request is itself XML, you can have the web service provider implement the Provider interface and feed the raw SOAP request directly to the DoubleIt.xsl file. See the CXF documentation for further information.
Sending and processing SOAP faults in web service calls
Following a useful article from Oracle on SOAP faults, this entry expands my intro DoubleIt web service to handle both "modeled" faults (those defined in the WSDL via
wsdl:fault elements) and "unmodeled" ones (generally, runtime exceptions that get processed as SOAPFaultExceptions by the SOAP client.) The finished source code can be obtained from GitHub by using either the download ZIP button or
git clone -v git://github.com/gmazza/blog-samples.git command.
Here, the DoubleIt web service provider has been adjusted to throw separate WSDL-defined exceptions if the client attempts to double odd numbers or the number 316. As these faults are WSDL-defined, the CXF WSDL-to-Java code generation tool activated by the service's pom.xml generates specific exception classes (DoubleOddNumberFault and DoubleNumber316Fault) for these that can be thrown by the WSP and caught by the SOAP client. Further, attempts to double 428 will throw a non-WSDL-defined runtime exception to show how the client receiving a generic SOAPFaultException.
WSDL changes: The modified WSDL contains two new
wsdl:messages for the new faults which you can see subsequently referenced in the
wsdl:binding sections. It's important to note each
wsdl:message needs to have a distinct value for its element (here, DoubleOddFault and Double316Fault defined in the
wsdl:types section), even though those two elements have identical contents. This is because the SOAP client uses the element name to determine the type of exception being thrown. (As you can see in the second and third SOAP responses at the bottom, the only difference between the two exceptions is the name of this element.) If you attempt to reuse the same element, results will be shaky. Also note that the
wsdl:fault definitions in the portType and binding sections are tied together via their name attributes.
Web service provider: The WSP has been updated to throw the two WSDL-defined faults as well as the RuntimeException for disallowed values.
The WAR submodule's cxf-servlet.xml also has a commented-out faultStackTraceEnabled setting that, if activated, results in the server's Java exception stack trace being sent to the client within every SOAP fault returned. It is seldom desired to leak internal data from the WSP to the SOAP client in such manner (and also makes for rather bloated SOAP responses), but the option is available and perhaps useful in some cases where the service and client are being maintained by the same team.
SOAP Client processing faults: Four calls are made, an acceptable call to double 22 and three other calls that will activate exceptions.
Review of SOAP requests and responses for each of the calls: Running the WSP and activating the client as explained in the original tutorial provides the following output from the client:
The number 22 doubled is 44 Odd number fault: Don't double odd numbers!; basic fault text: 11 is an odd number 316 Fault: Don't double 316!; basic fault text: Attempt was made to double 316 SOAPFaultException: Ha ha! Didn't tell you 428 can't be doubled either!
Below shows the first SOAP request (the other three differ only by the number being doubled) as well as the SOAP responses for each of the four calls.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:DoubleIt xmlns:ns2="http://www.example.org/schema/DoubleIt"> <numberToDouble>22</numberToDouble> </ns2:DoubleIt> </soap:Body> </soap:Envelope> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:DoubleItResponse xmlns:ns2="http://www.example.org/schema/DoubleIt"> <doubledNumber>44</doubledNumber> </ns2:DoubleItResponse> </soap:Body> </soap:Envelope> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <soap:Fault> <faultcode>soap:Server</faultcode> <faultstring>Don't double odd numbers!</faultstring> <detail> <ns2:DoubleOddFault xmlns:ns2="http://www.example.org/schema/DoubleIt"> <errorDetails>11 is an odd number</errorDetails> </ns2:DoubleOddFault> </detail> </soap:Fault> </soap:Body> </soap:Envelope> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <soap:Fault> <faultcode>soap:Server</faultcode> <faultstring>Don't double 316!</faultstring> <detail> <ns2:Double316Fault xmlns:ns2="http://www.example.org/schema/DoubleIt"> <errorDetails>Attempt was made to double 316</errorDetails> </ns2:Double316Fault> </detail> </soap:Fault> </soap:Body> </soap:Envelope> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <soap:Fault> <faultcode>soap:Server</faultcode> <faultstring>Ha ha! Didn't tell you 428 can't be doubled either!</faultstring> </soap:Fault> </soap:Body> </soap:Envelope>
Deploying and Using a CXF Security Token Service (STS)
Summary: In this tutorial we'll be creating a CXF Security Token Service (STS) and show how to access the STS using a CXF web service client (WSC). The client will authenticate with the STS to obtain SAML tokens that will subsequently be used to authenticate/authorize SOAP requests to a CXF web service provider (WSP) that trusts the STS.[Read More]
Using Apache CXF to access Salesforce Marketing Cloud SOAP API
I've created a SOAP client using Apache CXF 3.1.x and Maven to access Salesforce Marketing Cloud's SOAP Web Services API, in particular the more generic Partner API. Salesforce's CXF sample, while it contains useful examples that go beyond my tutorial, uses the decade-old CXF 2.0.2 with Apache Ant, so I decided to post a more modern sample. The SOAP client in this tutorial creates a DataFolder with two subfolders in Email Studio of your Marketing Cloud account, something simple to confirm ability to make SOAP calls as well as use for starting boilerplate for your own projects. The tutorial source code can be obtained from GitHub by using either the download ZIP button or
git clone -v git://github.com/gmazza/blog-samples.git command.
If you're new to SOAP, you may wish to review my intro web service tutorial.
The project has the following submodules, please take note of the changes needed for the code to work in your environment:
exacttarget-jaxws - This submodule primarily contains just the ExactTarget WSDL. The pom for this project uses CXF's wsdl-to-java generator to read the WSDL and from that generate the 300+ JAX-WS and JAXB Java classes used for accessing the Salesforce SOAP endpoint. This submodule in turn gets included as a dependency by the other two modules. The sample project does not contain the WSDL, as it is dependent on the instance (S1, S4, S6, S7, etc.) that you're using -- Salesforce support can help you if you're unsure which one. If wishing to run the sample, view the appropriate WSDL link in a browser, save it as text, and place it as "etframework.wsdl" in the src/main/resources folder of this submodule.
salesforce-access-lib - This is (the beginnings of) a library you can include in your application to make the Salesforce SOAP API calls, as stated earlier it just creates three data folders. You'll see that most SOAP actions in Salesforce involve repetitive configuration calls (for example, creating a DataFolder requires specifying several properties for it before saving) so that's it advantageous to factor out this configuration into service methods that you can efficiently call as needed.
testapp - This is a simple command-line application that calls a method in salesforce-access-lib to create the data folder. It uses Spring configuration to handle the UsernameToken (username and password) security requirements. To run the testapp:
After you've made the above-specified modifications, from the root folder run the Maven
mvn clean install from a command-line prompt to build all the submodules. You may then navigate to the testapp folder and run
mvn exec:exec to run the testapp and create the sample data folders that you'll be able to see once you log into Email Studio (Contents -> My Emails section). They can be deleted by right-clicking on them within Email Studio.
For further help on accessing the SOAP API and working with its objects and methods the Salesforce StackExchange is a good place to post questions.
|« May 2017|