Remote Proxy


The Remote Proxy also stands in for an object, but in this case the subject is remote. This could be a separate SWF file, an XML file, a Flash Remoting service, a SOAP or REST service, or any number of other type of services. The Remote Proxy acts as a local representative to this remote object. It has the same public methods as the remote resource and delegates requests to that resource. It also handles the communication with the remote resource.

Flickr Search Proxy Example

Flickr (www.flickr.com) is a popular online photo-sharing site. In this example, we're going to write a simple Remote Proxy to Flickr's search API. The proxy will implement a search method and handle the communication with the Flickr API. It will then broadcast an Event.COMPLETE or an ErrorEvent.ERROR event with the result.

Note

The Flickr examples in this chapter require that you apply for a key to access the Flickr API. In the examples, you'll need to replace the text <ADD_YOUR_KEY_HERE> with your Flickr API key. You can apply for anAPI key at the following URL: http://www.flickr.com/services/api/misc.api_keys.html.


First, create a new ActionScript project called RemoteProxyExample.

Creating the Search Proxy

The PhotoSearchProxy class takes local requests using its search method and relays them to the remote Flickr API. Flickr offers a few different flavors of its API. For our example, we're using the REST API. This API is just a simple HTTP GET request that returns an XML result. The parameters are sent in the query string of the request.

package com.peachpit.aas3wdp.proxypattern {    import flash.events.DataEvent;    import flash.events.Event;    import flash.events.EventDispatcher;    import flash.net.URLLoader;    import flash.net.URLRequest;    public class PhotoSearchProxy extends EventDispatcher {       private static const API_KEY:String = "<ADD_YOUR_KEY_HERE>";       private static const FLICKR_URL:String = " http://api.flickr.com/services/rest/";       public function PhotoSearchProxy() {}       private function onComplete(event:Event):void {          dispatchEvent(new DataEvent(Event.COMPLETE, false, false,                        XML(event.target.data));       }       public function search(userId:String, tags:String):void {          var loader:URLLoader = new URLLoader();          var request:URLRequest = new URLRequest(PhotoSearchProxy.FLICKR_URL  +             "?method=flickr.photos.search&user_&tags=" + tags +             "&api_key=" + PhotoSearchProxy.API_KEY);          loader.addEventListener(Event.COMPLETE, onComplete);          loader.load(request);       }    } }


Using the Search Proxy

To test the remote search proxy, we just create a new instance of the proxy (PhotoSearchProxy), register the complete and error events, and make a call to the search() method, like this:

package {    import com.peachpit.aas3wdp.proxypattern. PhotoSearchProxy;    import flash.display.Sprite;    import flash.events.DataEvent;    import flash.events.Event;        public class RemoteProxyExample extends Sprite {       public function RemoteProxyEmaple() {          var flickr:PhotoSearchProxy = new PhotoSearchProxy();          flickr.addEventListener(Event.COMPLETE, onComplete);          flickr.search("", "yellow");       }       private function onComplete(event:DataEvent):void {          trace(event.data);       }    } }


When you debug this example in Flex Builder, you'll see the XML result from the Flickr REST request output to the debug console.

The flash.utils.Proxy

In ActionScript 1 and 2, there was a method in the built-in Object class called __resolve. By overriding this method, you could capture any call made on that object that was undefined, including both properties and methods. The most common implementation of this feature was the Remoting and Web Service frameworks that used __resolve to proxy operations on remote methods.

This feature has grown up a bit in ActionScript 3.0 and is now encapsulated in the flash.utils.Proxy class. This class is never used directly, but is instead extended and its methods overridden. In Chapter 4, "Singleton Pattern," we used the Proxy class to capture calls to undefined properties and instead return a value from an XML configuration file. This was achieved by overriding the getProperty() method. To capture calls to undefined methods, we need to override the callProperty() method. In the next example, we will capture the calls to undefined methods to proxy those calls to the remote Flickr API. This will allow us to create a more flexible proxy.

Note

This chapter isn't a reference about the built-in Proxy class. This example illustrates the relationship of the Proxy class and the Proxy pattern, not a comprehensive description of how to use the Proxy class.


We just created a remote proxy example that calls a remote search method in the Flickr API, but what if we want to implement all the photo operations available in the Flickr API? We could systematically add each method to the PhotoSearchProxy class. This would be perfectly acceptable. But if we didn't know all the remote operations, or Flickr was continuously adding new operations, the PhotoSearchProxy class would fall short The solution is to use the built-in flash.utils.Proxy class.

Creating the Remote Photo Proxy

By extending the built-in Proxy class, we can catch calls to undefined methods and relay them to the remote Flickr API. This is a fairly simple process:

  1. We must make our class dynamic so that a call to an undefined method is allowed.

  2. The class needs to extend flash.utils.Proxy and override the callProperty() method. Each time a call to an undefined method is made on our class, we can catch it in callProperty().

  3. We must implement the IEventDispatcher interface and add the EvenTDispatcher functionality through composition. In our first Remote Proxy example, we extended Eventdispatcher, but we can't do that this time because we're extending Proxy and multiple inheritance is not permitted in ActionScript 3.0.

There is one minor catch: The call to Flickr requires us to format the method parameters in a query string with name/value pairs. However, ActionScript doesn't have named parameters at runtime, so we need to find out what every parameter's name is. Fortunately, the Flickr API has a reflection method flickr.reflection.getMethodInfo that allows us to get all the parameters for a given method in the API. So when a call to an undefined method is made, we can save the parameters and extract the method name. Then we can make a reflection call based on the method name. When the reflection call is returned, we can match the saved parameters with their names from the reflection result to generate the query string needed to make the original operation. Here's how that logic works:

package com.peachpit.aas3wdp.proxypattern {    import flash.events.DataEvent;    import flash.events.Event;    import flash.events.EventDispatcher;    import flash.events.IEventDispatcher;    import flash.net.URLLoader;    import flash.net.URLRequest;    import flash.utils.flash_proxy;    import flash.utils.Proxy;    dynamic public class PhotoProxy extends Proxy implements IEventDispatcher {       private static const API_KEY:String = "<ADD_YOUR_KEY_HERE>";       private static const FLICKR_URL:String = " http://api.flickr.com/services/rest/";       private var eventDispatcher:EventDispatcher;       private var pendingArgs:Array;       public function PhotoProxy() {          eventDispatcher = new EventDispatcher();       }       // The following event handler is called when the       // reflection call is made to the Flickr API. The results       // of this call tell us what to name the original request's       // parameters and allows us to build a query string with       // name/value pairs       private function onReflectionComplete(event:Event):void {          var queryString:String = "";          var reflection:XML = XML(event.target.data);          var methodArguments:XMLList = reflection.arguments.argument;          for(var i:Number = 0; i < pendingArgs.length; i++) {             if(pendingArgs[i] != null) {                queryString += "&" + methodArguments[i].@name.toString() + "=" +                pendingArgs[i];             }          }          var loader:URLLoader = new URLLoader();          var request:URLRequest = new URLRequest(PhotoProxy.FLICKR_URL + "?method=" +             reflection.method.@name.toString() + queryString);          loader.addEventListener(Event.COMPLETE, onComplete);          loader.load(request);       }       // This event handler is called when the real result is       // received from the Flickr API. It simply broadcasts this       // data as a DataEvent event.       private function onComplete(event:Event):void {          dispatchEvent(new DataEvent(Event.COMPLETE, false, false,          XML(event.target.data)));       }           // This is the method that captures the request. It is a       // part of the flash.utils.Proxy class.       flash_proxy override function callProperty(methodName:*, ...args):* {           pendingArgs = args;           pendingArgs.unshift(PhotoProxy.API_KEY);           var loader:URLLoader = new URLLoader();           var request:URLRequest = new URLRequest(PhotoProxy.FLICKR_URL +              "?method=flickr.reflection.getMethodInfo&method_name=flickr.photos." +              methodName.toString() + "&api_key=" + PhotoProxy.API_KEY);           loader.addEventListener(Event.COMPLETE, onReflectionComplete);           loader.load(request);           return methodName.toString();       }       public function addEventListener(type:String, listener:Function,          useCapture:Boolean = false, priority:int = 0, weakRef:Boolean = false):void {          eventDispatcher.addEventListener(type, listener, useCapture, priority, weakRef);       }       public function dispatchEvent(event:Event):Boolean {          return eventDispatcher.dispatchEvent(event);       }       public function hasEventListener(type:String):Boolean {          return eventDispatcher.hasEventListener(type);       }       public function removeEventListener(type:String, listener:Function,          useCapture:Boolean = false):void {          eventDispatcher.removeEventListener(type, listener, useCapture);       }       public function willTrigger(type:String):Boolean {          return eventDispatcher.willTrigger(type);       }    } }


Using the Photo Proxy

To use this photo proxy, we need to slightly modify the main class from the first remote proxy example. The only change is the reference to the PhotoProxy class in place of the PhotoSearchProxy class. Also, to show that all the photo operations are now available through this proxy, we will now call the getRecent() operation.

package {    import com.peachpit.aas3wdp.proxypattern.FlickrResultEvent;    import com.peachpit.aas3wdp.proxypattern.PhotoProxy;    import flash.display.Sprite;    import flash.events.Event;    import flash.events.DataEvent;        public class RemoteProxyExample extends Sprite {       public function RemoteProxyExample() {          var flickr:PhotoProxy = new PhotoProxy();          flickr.addEventListener(Event.COMPLETE, onComplete);          flickr.getRecent();       }       private function onComplete(event:DataEvent):void {          trace(event.data);       }    } }





Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net