/customers/iconara.net/iconara.net/httpd.www/blog/wp-content/plugins/wp-super-cache/wp-cache-phase1.php Iconara » Finding cover art with Flex and Amazon WebServices

Finding cover art with Flex and Amazon WebServices

There are lots of examples of how to access Amazon’s WebServices (AWS), and their own documentation is extensive. However a quick google on “Flex Amazon WebServices” doesn’t turn up many results. Amazon’s own examples are all for other platforms.

Let’s put together some code that searches Amazon for CD cover art.

This article was written quite a while ago, but I never got around finishing it. It outlines the basics on how to implement something like the component I wrote about in Amazon image finder component for Flex.

Trial and error

I’ve used AWS before in PHP and Java, those times I used the REST interface, but this time I thought I should try out SOAP, since it seems to be quite simple in Flex. However, I got problems that I can’t figure out how to solve. The request is supposed to be a complex type and either I have misunderstood how to set it up, or Flex serializes the request wrong. Anyway, I switched to the REST interface and HTTPService instead of WebService and I got it working in no time. Since AWS is read-only the difference between SOAP and REST is minimal.

If you have a working example of using Flex’ WebService and AWS, please leave a comment, I would love to see how to do it.

Handling concurrency

What other tutorials don’t show you is how to handle multiple concurrent requests. Since the requests are asynchronous there is really no telling which request’s response you’re handling in your result event handler.

Fortunately AWS has a feature which echoes the request parameters back to you in the response. So what you do is add your own internal request id to the request, save the callback (or ideally some other method of returning the result) in a dictionary so that you can look it up with the internal request id once the response comes back.

Let’s look at the code.

Setup

This is the boring necessities, declare and initialize the service and result handlers dictionary and add an event handler to listen for responses. I have declared the AWS namespace as a static constant so that it will be easily available throughout the code.

private static const aws : Namespace =
  new Namespace("http://webservices.amazon.com/AWSECommerceService/2005-10-05");

private var aws : HTTPService;

private var resultHandlers : Dictionary;

// ...

resultHandlers = new Dictionary();

aws = new HTTPService(); aws.url = "http://ecs.amazonaws.co.uk/onca/xml"; aws.resultFormat = "e4x";

aws.addEventListener(ResultEvent.RESULT, onResult);

Fire off the query

Your client code will search for a cover art picture by calling the find method with a query string and a callback. The callback function should take a single argument, an array (which will contain the URL’s of all the found CD’s).

Using a callback is not always ideal, but it’s minimal, and that means we can discuss other things more. You can change the implementation to suite your needs.

public function find( query : String, callback : Function ) : void {
  // this creates a unique request id
  var requestId : String = getTimer() + "/" + query;

// associate the callback with the request resultHandlers[requestId] = callback;

var parameters : Object = { Service: "AWSECommerceService", AWSAccessKeyId: AWS_ACCESS_KEY, Operation: "ItemSearch", Keywords: query, SearchIndex: "Music", ResponseGroup: "Small,Images", InternalRequestId: requestId // notice our own non-standard request parameter }

aws.send(parameters); }

Handling the response

Once the response comes back, it’s a matter of finding our request id, get the callback registered for the request and pass the parsed results to it.

There are some hairy E4X lines in this code, just as with regular expressions I find it hard to make them readable.

private function onResult( event : ResultEvent ) : void {
  var result : XML = event.result as XML;

var requestId : String = findInternalRequestId(result);

var handler : Function = resultHandlers[requestId];

delete resultHandlers[requestId];

handler(findImageUrls(result)); }

private function findInternalRequestId( result : XML ) : String { default xml namespace = aws;

var args : XMLList = result.OperationRequest.Arguments;

return (args.Argument.(@Name == "InternalRequestId").@Value)[0]; }

private function findImageUrls( result : XML ) : Array { default xml namespace = aws;

var imageUrlNodes : XMLList = (result.Items.Item.LargeImage.URL);

var imageUrls : Array = new Array();

for each ( var urlNode : XML in imageUrlNodes ) { imageUrls.push(urlNode.text()); }

return imageUrls; }

Conclusion

That’s more or less all that’s to it. Using the code above you can search for anything available from Amazon.

For an example on how you can use this, see my earlier post Amazon image finder component for Flex.

10 Responses to “Finding cover art with Flex and Amazon WebServices”

  1. Maz Says:

    Seems that Flex2 (even Moxie) do not handle too complex ComplexTypes… (particularly WSDL types with attributes in addition to elements)

    If you work out anything, tell me :)

    maz.spam_AT_gmail_DOT_com (replace words between underscores)

  2. Theo Says:

    Sorry, I never pursued the matter further. Amazon’s REST interface was much easier to work with.

  3. Mike Says:

    I also have had an issue trying to communicate with AWS using soap.

    It does seem to connect ok.. but I keep getting an error back saying that my ItemSearch request needs to define at least one of these parameters: AWSAccessKeyId, SubscriptionId… but I have!

  4. Theo Says:

    Can’t remember now, but it might have been the same error that made me give it up an use the REST interface instead. There is one part of the request that is supposed to be a “complex type”, probably one of those keys must be present in that for it to work, but since Flex doesn’t serialize the complex type right the key isn’t there.

  5. Mike Says:

    Hey, Thanks for your reply.

    I worked out my problem with Webservices. My problem was that in Flex builder 3 (beta, I might add) you can’t just go AWS.ItemSearch.send(mySearchObject). (*Note – you can in FB2)

    You have to go AWS.ItemSearch.arguments = mySearchObject; AWS.ItemSearch.send();

    FB3 does actually deserialize the result quite well when the resultFormat is “object”.

    I am now experimenting with the resultFormat “e4x” but I am having problems getting data out of the returned XMLList.

    All the examples I have looked at say to use: var myXML:XMLList = event.result..Item;

    BUT this doesn’t work. The data is there but the variable myXML becomes a null object, and no matter whether you are using HTTPService or WebService the e4x result is the same.

    I tried piecing together your code above. I couldn’t get everything to compile because the Dictionary component is missing… but I tried your line ( var args : XMLList = event.result.OperationRequest.Arguments;) on the HTTPService result and that didn’t work either.

    Is there something I’m missing?

    I’ve got the webservice part to work fine. I just can’t seem to successfully parse the “e4x” results in flex.

  6. 2pha Says:

    I am having the same problem as above I think.

    the problem is with:

    var imageUrlNodes : XMLList = (result.Items.Item.SmallImage.URL);

    tracing the xml before it is fine, it outputs the xml returned from amazon. But the items are not populating the XMLList for some reason.

  7. Theo Says:

    Could it be some kind of namespace problem? I haven’t used the code since I wrote this article, so I’m not sure, but try to replace

    default xml namespace = aws;

    with

    use namespace aws;

    That might work better. Or do this:

    result.aws::Items.aws::Item.aws::SmallImage.aws::URL

    “aws” is the namespace which Amazon ECS uses, declared as

    namespace aws = “http://webservices.amazon.com/AWSECommerceService/2005-10-05″;

  8. Theo Says:

    Hmm, when I look at the namespace it strikes me that Amazon regularly updates the namespace URI with a new date. Check the return data from ECS and see what the current namespace is, that might be the problem you’re having.

  9. 2pha Says:

    ahhh. I changed the aws:Namespace to awsNS:Namespace

    because there is also a aws:HTTPservice

    Everythings working now :)

    Thanks alot for sharing you code

  10. Sintara Says:

    Thanks for making this available!

Leave a Reply