Parameterize your SOAP address

The problem for today is how to change the SOAP address used by a PDF form. The common scenario is that users want to have one SOAP address when developing and testing their form, but a different address when deploying it.  In fact, depending on your server configuration, you may want to deploy the same form on different servers with different SOAP addresses.

Rather than change the form for each server we deploy to, we prefer to parameterize the SOAP address.   There are two main challenges for solving this problem:

  1. In a form definition, the WSDL/SOAP definition is read-only
  2. For forms that deal with sensitive data, any SOAP addresses need to be covered by certification in order to prevent any ‘man-in-the-middle’ attacks

Weather Report

For today’s sample, I used a weather report SOAP service provided by www.webservicex.net.  This service: http://www.webservicex.net/WCF/ServiceDetails.aspx?SID=50 allows you to get a six day forecast for US cities based on a zip code.  Here is the corresponding WSDL.

For more background on using a WSDL definition from a PDF form you can check out this previous entry.

Cloning the Connection

There are two ways to execute a SOAP operation:

  1. With declarative XFA markup (the <execute> element)
  2. With a script call on the connection object (connection.execute())

By default, Designer will generate the <execute> markup.  If you want to modify the SOAP address, you need to use the script version. To get past the read-only restriction on connection objects, we first clone the connection.  With a read/write cloned connection we can change the SOAP address.  The code looks like this:

var vConnection = xfa.connectionSet.WeatherForecast.clone(true);
vConnection.soapAddress.value = "<revised soap address>";

Protecting the Address

In our scenario where we design the form once and want it to work with multiple SOAP addresses, we need to figure out how to pass the address to the form.  The obvious solution is to embed the SOAP address in the form data.  This strategy works fine if your form is accessing a weather service.  But if your form is interacting with a financial institution we need something more secure.  We need to deliver the form and the SOAP address in a manner that cannot be tampered.

For sensitive forms we recommend certification.  A certified form cannot be modified without the end user becoming aware that the form definition has changed. 

Parameterized Submit URL

We’ve previously solved a similar problem by allowing submit URLs to be parameterized.  The strategy in this case was to embed an array of submit URLs in the config data.  Then in the form data we inject an index value to indicate which submit URL to use.  The config grammer looks like:

<config xmlns="http://www.xfa.org/schema/xci/2.8/">
   <present>
      <submitUrl>http://service1.submit.net/</submitUrl>
      <submitUrl>http://service2.submit.net/</submitUrl>
      <submitUrl>http://service3.submit.net/</submitUrl>
      …
  
</present>
</config>


Then in our form data we add a data element that selects the appropriate URL:

<xfa:datasets xmlns:xfa=”http://www.xfa.org/schema/xfa-data/1.0/”>
  <variables xmlns=”http://ns.adobe.com/server-context-data/”>
    <
submitUrlIndex>2</submitUrlIndex>
 
</variables>

</xfa:datasets>

With submit URLs, this all works automatically.  When you leave the submit URL unspecified in your form definition, Reader will automatically look up the submitUrl from config.

Reduce, Re-use, Recycle

We were tempted to add new grammar and new code to Reader to do the same for SOAP addresses as we did for submit URLs — but we thought better of it.  We can make this work for SOAP addresses by re-using the submitUrl grammar and adding a bit of JavaScript to do the lookup.

Assuming we now populate the config <submitUrl> grammar with SOAP addresses, we’ll use a new data variable for indexing:

<xfa:datasets xmlns:xfa=”http://www.xfa.org/schema/xfa-data/1.0/”>
  <variables xmlns=”http://ns.adobe.com/server-context-data/”>
   <soapUrlIndex connection="WeatherForecast">2</soapUrlIndex>
 
</variables>

</xfa:datasets>

Of course, this won’t work automagically as it does with submitUrl.  We need to write the script to retrieve the SOAP address.  That’s about 25 lines of script.  Have a look at the click event of the button in the sample form to see how this works.

The Deep End

Did you notice all those nice weather images that show up in the forecast?  You might have assumed that the image data was returned to us by the SOAP call.  Well… not exactly.  The SOAP response gave us the image URLs.   And as you know, a PDF cannot load an external image.  The solution is to embed all the images in the PDF. Yep, all 339 images used by the SOAP service are embedded in the PDF.  Fortunately they’re pretty small images.  In a previous post I talked about the benefits of linked images: Linked vs Embedded Template Images

There is a nice trick to getting all those images embedded.  When you are generating your form on the server, your form data may contain URL references to images.  The syntax looks like:

<img xfa:contentType="image/jpg"
                                href="http://forecast.weather.gov/images/wtf/blizzard.jpg"/>

When we create the PDF on the server (or in Designer preview) all image hrefs found in the data are embedded in the PDF and are indexed by their URL.  When the SOAP service returns an image URL to us, we simply assign that value to an image field and the field automatically gets connected to the image embedded in the PDF.

vNewDay.weatherImage.value.image.href = vDay.WeatherImage.rawValue;

Have a look at the form:ready script on the zip code field for the script that works with the SOAP response.

Here is the data file for pre-loading all those images.  Be warned that using this data file will make your PDF generation *very* slow.