Tuesday, September 27, 2011

Loosly Coupled Server Query List Manager for AS3

Well, after much deliberation, the solution was to stick with an external list manager to control Server Queries.  Adding the list management to the ServerQuery class module proved to be more trouble than it was worth since each query would need to track its own status and timing.  This could be better served with an external management class.

All that was needed to support an external manager was a simple callback function to allow the manager to know when queries finished execution.

Here is the pre-test code for the ServerQuery and ServerQueryList classes:

 import flash.events.*;
 import flash.net.*;

 //
 // a static helper class to queue queries to the server, all in one place... reports average server response time as a bonus!
 //
 public class ServerQueryList {
 
  static var serverQs : Array = new Array(); // array of ServerQuery objects
  static var _networkLatency : int = 0;  // the average response time from the server
 
  function ServerQueryList() {
 
  }
 
  // request data from the server... takes URLstring and callback function as paramaters
  static function QueryServer(req:String, callback:Function, bMeasureLatency:Boolean=false) {
   // create a new ServerQuery object
   var sq : ServerQuery = new ServerQuery(req, callback);
  
   // set the callback to our private function
   sq.setListCallback(sqCallback);
  
   // push it onto our list
   newindex = serverQs.push(sq);
  
   // pass along the index... push actually adds to end of array not to beginning ala a stack... confusing function name there
   sq.setIndex(newindex);
  
   sq.Load(); // do it!
  
  }
 
 
  private function sqCallBack(target:ServerQuery) {
   // our target has returned...
  
   // record the round trip time and then delete it from the array
  
   var sqIndex = target.getIndex();
  
   if (sqIndex != -1 && sqIndex < serverQs.length) { // validate index
    accumulateLatency(ServerQuery(serverQs[sqIndex]).getResponseTime());
   
    //do the cleanup!
    ServerQuery(serverQs[sqIndex]).kill();
   
    delete serverQs[sqIndex];
    serverQs[sqIndex] = null;
   
    serverQs.splice(sqIndex, 1);
   }
  
  
  }
 
  private function accumulateLatency(newResponseTime:int) {
   // add the response time to our aggregate response time
   if (_networkLatency != 0)
    _networkLatency = (_networkLatency + newResponseTime) / 2;
   else
    _networkLatency = newResponseTime;
  }
 
 
 } // end ServerQueryList


 import flash.events.*;
 import flash.net.*;


 //
 // actually gunna leave this one as a generic query class.. will make a queue class to manage these
 // a helper class to queue queries to the server, all in one place... reports average server response time as a bonus!
 //
 public class ServerQuery {
 
  private var _urlloader : URLLoader;
  private var _callback : Function; // supplied by caller
  private var _urlrequest : URLRequest;
 
  private var _listCallback : Function; // supplied by query list manager
 
  private var _responseTime : int;  // milliseconds for round trip
  private var _opened : Number;   // time when connection opened
  private var _closed : Number;   // time when response has arrived
 
  //TODO: add timer for timeout failure
 
  private var _debugStr : String;
 
  private var _index : int;     // the index of this query within the external list of queries
 
  public function ServerQuery(req:String, callback:Function) {
   _debugStr = "";
   _urlloader = new URLLoader();
   configureListeners(_urlloader);
   _urlrequest = new URLRequest(req);
   _callback = callback;
   _urlloader.addEventListener(Event.COMPLETE, CTQueryPreCallback);
   _responseTime = -1;
   _index = -1;
   _listCallback = null;
  }
 
  public function setListCallback(newFunc:Function) {
   _listCallback = newFunc;
  }
 
  public function setIndex(newIndex:int) {
   _index = newIndex;
  }
 
  public function getIndex() : int {
   return _index;
  }
 
 
  public function getResponseTime() : int {
   return _responseTime;
  }
 
  public function Load() {
   try {
    var date : Date = new Date();
    _opened = date.getTime();
    _urlloader.load(_urlrequest);
   } catch (error:Error) {
    _debugStr += "ServerQuery: Unable to load requested document";
   }  
  }
 
  public function getDebugReport() : String {
   return _debugStr;
  }
 
  public function kill() {
   // do some cleanup
   delete _urlloader;
   delete _urlrequest;
  }
 
  private function CTQueryPreCallback(e:Event) {
   deconfigureListeners(_urlloader);
  
   var date : Date = new Date();
   _closed = date.getTime();
  
   _responseTime = _closed - _opened;  
  
   // inform the list manager, if any, we have returned.. pass it our this pointer
   if (_listCallback != null)
    _listCallback(this);
   if (_callback != null)
    _callback(e); // call our stored callback
    

  }
 
     private function configureListeners(dispatcher:IEventDispatcher):void {
            //dispatcher.addEventListener(Event.COMPLETE, completeHandler);
            //dispatcher.addEventListener(Event.OPEN, openHandler);
            //dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
 
  private function deconfigureListeners(dispatcher:IEventDispatcher) {
            dispatcher.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.removeEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            dispatcher.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);  
  }
 
     private function securityErrorHandler(event:SecurityErrorEvent):void {
            trace("ServerQuery: securityErrorHandler: " + event);
   _debugStr += "securityErrorHandler: " + event + "\n";
        }

        private function httpStatusHandler(event:HTTPStatusEvent):void {
            trace("ServerQuery: httpStatusHandler: " + event);
   _debugStr += "httpStatusHandler: " + event + "\n";
        }

        private function ioErrorHandler(event:IOErrorEvent):void {
            trace("ServerQuery: ioErrorHandler: " + event);
   _debugStr += "ioErrorHandler: " + event + "\n";
  }
 
 } // end class

1 comment:

  1. OMG WHY AM I TREADING DOWN THIS ROAD!

    I am still stuck here...

    http://devcenter.heroku.com/articles/facebook#what_is_heroku

    ReplyDelete