10.8 Datatype Conversions

Web services have specific simple types that are native to SOAP and other complex types that you can define. The SOAP datatypes and their ActionScript equivalencies are listed in Table 10-3.

Table 10-3. Datatype conversion between ActionScript and SOAP

ActionScript

SOAP

Null

Null

Undefined

Null

Boolean

Boolean

Number

Decimal, Float, Double, Integer, Int

String

String

Date

DateTime

Array (numeric index)

Array

Associative array

Complex type

RecordSet

N/A

Object

Complex type

ColdFusion also supports a QueryBean datatype, which allows you to transfer query objects as results from a web service. It is advisable, however, to create web services that will be more universally readable, such as an array. Not all consumers of web services will be able to access a QueryBean.

10.8.1 Passing Complex Datatypes to and from Web Services

Sending a complex datatype to a web service is sometimes as simple as defining it in the client-side ActionScript and passing it as an argument. An example of a web service that accepts a complex object as an argument and returns a complex object as a result is the Amazon.com web service. Unfortunately, this is not a service that is usable in different server environments. Example 10-6 works in ASP.NET and PHP environments only. The Flash Remoting gateway for Java and ColdFusion (essentially the same gateway) seems to have problems with this web service.

To make use of the Amazon.com web service, you must go to http://www.amazon.com/webservices and sign up for a free developer's kit. You are issued a developer's token that can be used as a key to use the service. Once you've done that, you are free to use their service in accordance with the licensing agreement.

One obvious use is to search Amazon.com's catalog of books. The Flash UI for Amazon.com web service example is shown in Figure 10-3. The source file, amazon.fla, can be downloaded from the online Code Depot.

Figure 10-3. The Amazon.com service using Flash Remoting
figs/frdg_1003.gif

We'll use the KeywordSearchRequest( ) method of the Amazon.com web service for this example. The method accepts an object with the following properties as an argument (see the comments in Example 10-6 or the Amazon.com documentation for more information about each argument):

keyword
page
mode
tag
type
sort (optional)
devtag

The response from the service is also in the form of an object. The returned object has the following properties:

ListName

Not used for the KeywordSearchRequest( ) method.

TotalResults

Contains the total number of search results (only 10 results are returned from the service, however).

Details

An array of objects containing the details of the books returned by the search. Each element of the array is an object that contains the following properties:

URL

Manufacturer

ASIN

ImageUrlSmall

ProductName

ImageUrlMedium

Catalog

ImageUrlLarge

Artists

ListPrice

Authors

OurPrice

ReleaseDate

UsedPrice

The Artists and Authors properties are arrays as well. The code shown in Example 10-6 creates the Flash MX interface to the Amazon.com web service, which takes a complex datatype as a parameter and returns a complex datatype to the caller. Because Amazon.com implements its own pageable results, we have to implement paging in our Flash movie to be able to display all of the results. The code is commented inline.

Example 10-6. Amazon.com web service implementing a keyword search (amazon.fla)
#include "NetServices.as"

if (!connected) {
  var connected = true;
  var gatewayURL = "http://localhost/flashremoting/gateway.aspx");
  NetServices.setDefaultGatewayURL(gatewayURL);
  var gatewayConnection = NetServices.createGatewayConnection( );
  var myService = gatewayConnection.getService(
   "http://soap.amazon.com/schemas2/AmazonWebServices.wsdl", this);
}

var keywordSearchPages = 0;
var KeywordRequestArgument = new Object( );

// Initialize the object with properties and methods
KeywordRequestArgument.init = function ( ) {
  this.keyword = "";           // Search word or words
  this.page = "1";             // Page number of results
  this.mode = "books";         // Type of product we are searching
  this.tag = "myassociateID";  // Amazon.com associate ID if you have one
  this.type = "lite";          // Type is either "lite" or "heavy"
  this.devtag = "yourtaghere"; // The Amazon developer tag that is issued to you
  this.version = "2.0";        // Version number of the Amazon web service
// This ends the properties for the object

  // Methods will be stripped off before being sent to the service
  this.setPageNumber = function (page) {this.page = page.toString( );};
  this.getPageNumber = function (page) {return this.page;};
  this.setKeyword = function (keyword) {this.keyword = keyword;};

  // Call the remote service
  this.callService = function (page) { 
    this.setPageNumber(page);
    pagedisplay_txt.text = "...working";
    myService.KeywordSearchRequest(this);
  };
};

KeywordRequestArgument.init( );  // Initialize the object

previous_pb.setClickHandler("previousPage");
function previousPage ( ) {
  var page = KeywordRequestArgument.getPageNumber( );
  // Decrement the page counter, but no less than 1
  page = (page-- < 1) ? 1 : page--;
  KeywordRequestArgument.callService(page);
}

next_pb.setClickHandler("nextPage");
function nextPage ( ) {
  var page = KeywordRequestArgument.getPageNumber( );
  // Increment the page counter, but no greater than total pages
  page = (page++ >= keywordSearchPages) ? keywordSearchPages : page++;
  KeywordRequestArgument.callService(page);
}

submit_pb.setClickHandler("getResults");
function getResults ( ) {
  keywordSearchPages = 0;
  KeywordRequestArgument.setKeyword(search_txt.text);
  KeywordRequestArgument.callService("1");
}

// Turn a URL into a clickable link
function makeLink(theText,theLink) {
  return '<font color="#00cc00"><a href="' + unescape(theLink)+ '" target="_blank">' + 
theText + '</a></font>';
}

function KeywordSearchRequest_Status (error) {
  trace(error.description);
}

// Display the results
function KeywordSearchRequest_Result (result) {
  results_txt.text = "";
  if (result.TotalResults == 0) {
    pagedisplay_txt.text = "No results";
  } else {
    keywordSearchPages = Math.ceil(result.TotalResults/10);
    var temp = "";
    var totalResults = (result.TotalResults < 10) ? result.TotalResults : 10;
    for (var i=0; i < totalResults; i++) {
    temp += makeLink(result.Details[i].ProductName, result.Details[i].Url)+
            "<br>";
    temp += "by " + result.Details[i].Authors.join(", ") + "<br>";
    temp += "List price: " + result.Details[i].ListPrice + "<br>";
    temp += "Amazon price: " + result.Details[i].OurPrice + "<br>";
    temp += result.Details[i].Manufacturer + ": " +
            result.Details[i].ReleaseDate + "<br>";
    temp += "<br>";
    }
    results_txt.htmlText = temp;
    pagedisplay_txt.text = "Page " + KeywordRequestArgument.getPageNumber( ) +
       " of " + keywordSearchPages;
  }
}

In this case, we attach the parameters required for the web service as properties of a generic Object, KeywordRequestArgument. The parameters are handled by the Flash Remoting adapter on the server and translated into the proper SOAP datatypes by the application server. An object is returned to the Flash movie and is parsed and displayed in the KeywordSearchRequest_Result( ) function.

Example 10-6 uses the ASP.NET gateway. No server-side code is necessary. For PHP, the code should work as written (because it passes the parameters to the service as properties of an object), provided you update the gateway URL to point to the PHP gateway.

I hope that Macromedia makes the future versions of Flash Remoting more consistent across server implementations so that services such as Amazon.com can be used by ColdFusion MX and J2EE servers as well.

10.8.2 Passing Simple Arrays to Web Services

Web services can supply many different types of results to consumers of those services. Many web services pass simple strings or simple values. In Example 10-6, we saw the Amazon.com web service, which passes a complex object, making it incompatible with ColdFusion MX and J2EE servers when using Flash Remoting.

An array is a basic datatype in most languages, and SOAP is no exception. The web service at http://www.communitymx.com/services/cmxfeed.wsdl passes an array of simple objects to the consumer. The web service lists articles and other content available at Community MX, a support site for Studio MX and other web technologies. The array contains objects with the following properties:

Title
Author
Category
Description
Keywords
Type_description
Url

The ActionScript code for the simple interface is shown in Example 10-7. Notice that the service method getContent( ) takes one argument: type. If you pass the argument "all", the service simply passes the latest content feed from Community MX. The interface consists of two text fields: results_txt and content_txt, with a scrollbar attached to content_txt. The source file, communitymx.fla, can be downloaded from the online Code Depot.

Example 10-7. Flash code for web service from Community MX (communitymx.fla)
#include "NetServices.as"

// Set up variables for the URL and service paths
var myURL = "http://localhost:8500/flashservices/gateway";
var servicePath = "http://www.communitymx.com/services/cmxfeed.wsdl";

// Define the custom SimpleResult class to display the results
function SimpleResult( ) { }
// Set up onResult( ) and onStatus( ) handlers as methods of SimpleResult class
SimpleResult.prototype.onResult = function (myResults) {
  results_txt.text = myResults.length + " records returned";
  var temp = "";
  for (var i=0; i < myResults.length; i++) {
    temp += makeLink(myResults[i].title,myResults[i].url) + "<br>";
    temp += "Author: " + myResults[i].author + "<br>";
    temp += "Category: " + myResults[i].category + "<br>";
    temp += "Description: " + myResults[i].description + "<br>";
    temp += "<br><br>";
    }
  content_txt.htmlText = temp;
};
SimpleResult.prototype.onStatus = function (myError) {
  results_txt.text = myError.description;
};
// Set the system status to be handled by the result status handler as well
System.onStatus = SimpleResult.prototype.onStatus;

// Make a clickable link out of the Title
function makeLink(theText,theLink) {
  var temp = '<font color="#00cc00"><a href="';
  temp += unescape(theLink);
  temp += '" target="_blank">' + theText + '</a></font>';
  return temp;
}

// Connection hasn't been initialized; create connection and service objects
if (initialized == null) {
  initialized = true;
  NetServices.setDefaultGatewayURL(myURL);
  var myConnection_conn = NetServices.createGatewayConnection( );
  var myService = myConnection_conn.getService(servicePath, new SimpleResult( ));
}

// Call the service on load
results_txt.text = "...working";
myService.getContent("all");

The content is displayed when the movie loads.

Example 10-7 uses the ColdFusion gateway. The ASP.NET version is identical, except for the path to the Flash Remoting adapter on the server. The PHP version needs to have the arguments to the service packed into an object, so replace the last line of Example 10-7 with these two lines:

var tempObj = {type:"all"};
myService.getContent(tempObj);

The J2EE and JRun versions need a JavaBean wrapper, as described earlier under Section 10.5. The JavaBean shown in Example 10-8 will work for this service after following the instructions outlined for Example 10-3 under Section 10.5.

Example 10-8. JavaBean wrapper code for the Community MX web service
// Use the same package as the wsdl2java-generated classes
package services;

public class CmxfeedBean implements java.io.Serializable {

  // Handle to the generated stub
  private services.CmxfeedCfcSoapBindingStub soap;

  // Empty constuctor
  public CmxfeedBean( ) throws java.net.MalformedURLException,
   org.apache.axis.AxisFault {
    final java.net.URL endPoint = new java.net.URL
     ("http://www.communitymx.com/services/cmxfeed.wsdl");
      soap = new services.CmxfeedCfcSoapBindingStub(endPoint,
      new org.apache.axis.client.Service( ));
    }

  // Public method to call the web services method getContent( )
  public Object[] getContent(String myArg) throws java.rmi.RemoteException {
    return soap.getContent(myArg);
  }
}


    Part III: Advanced Flash Remoting