package com.ericfeminella.rpc.instrumentation
{
import com.ericfeminella.diagnostics.Execution;
import flash.events.EventDispatcher;
import mx.rpc.AsyncToken;
import mx.rpc.IResponder;
import mx.rpc.Responder;
import mx.utils.StringUtil;
/**
*
* Dispatched when the operation this <code>RPCDiagnostics</code>
* instance is monitoring has completed.
*
* @eventType RPCDiagnosticsEvent.EXECUTION_COMPLETE
*
*/
[Event(name="executionComplete", type="com.ericfeminella.rpc.instrumentation.RPCDiagnosticsEvent")]
/**
*
* <code>RPCDiagnostics</code> provides an API which "wraps" an
* <code>AsyncToken</code> instance in order to provide diagnostic
* information regarding the elapased time for an individual RPC
* service operation.
*
* <p>
* Clients are intended to implement <code>RPCDiagnostics</code>
* in one of two ways:
* <ul>
* <li> As an <code>AsyncToken</code> would be implemented</li>
* <li> As an entry point unto an <code>AsyncToken</code></li>
* </ul>
* </p>
*
* <p>
* Each implementation ensures the first <code>Responder</code>
* instance in the <code>AsyncToken</code> is a reference to
* the <code>RPCDiagnostics</code> instance, so as to ensure
* precise accuracy.
* </p>
*
* @example The following example demonstrates a client implementation
* of <code>RPCDiagnostics</code> against an <code>HTTPService</code>
* which is implemented as an <code>AsyncToken</code> would be.
*
* <listing version="3.0">
*
* var call:RPCDiagnostics = null;
* call = new RPCDiagnostics( service.send(), "operationName" );
* call.addResponder( this );
*
* </listing>
*
* @example The following example demonstrates a client implementation
* of <code>RPCDiagnostics</code> against an <code>HTTPService</code>
* which is implemented as an entry point unto an <code>AsyncToken</code>.
*
* <listing version="3.0">
*
* var call:AsyncToken = RPCDiagnostics.monitorToken( service.send(), "operationName" );
* call.addResponder( this );
*
* </listing>
*
* @see http://livedocs.adobe.com/flex/gumbo/langref/mx/rpc/AsyncToken.html
*
*/
public class RPCDiagnostics extends EventDispatcher
{
/**
*
* Defines the default message which conveys the execution
* results.
*
* @example The following example demonstrates an invocation
* against an operation called "getItems", and the message
* which is produced upon a result or fault event.
*
* <listing version="3.0">
*
* var call:RPCDiagnostics = null;
* call = new RPCDiagnostics( service.send(), "getItems" );
* call.addResponder( this );
*
* // Operation 'getItems' executed in 324 milliseconds
*
* </listing>
*
*/
protected static const _defaultMessage:String = "Operation '{0}' executed in {1} milliseconds";
/**
*
* Defines a reference to the <code>AsyncToken</code> instance
* from which this objects will monitor an operations execution
* time.
*
*/
protected var token:AsyncToken = null;
/**
*
* Defines the <code>operationName</code> which this invocation
* is associated with.
*
*/
protected var operationName:String = null;
/**
*
* Defines a reference to the <code>Execution</code> object which
* is responsible for determining the execution time for this
* operation.
*
*/
protected var execution:Execution = null;
/**
*
* <code>RPCDiagnostics</code> constructor initializes monitoring
* of an <code>AsyncToken</code>.
*
* @param The <code>AsyncToken</code> instance which is to be monitored
* @param The operation name which is being monitored
*
*/
public function RPCDiagnostics(token:AsyncToken, operationName:String)
{
this.execution = new Execution();
this.token = token;
this.operationName = operationName;
initializeResponders();
}
/**
*
* <code>addResponder</code> adds an <code>mx.rpc.IResponder</code>
* implementation to the underlying <code>AsyncToken</code> instance.
*
* @param IResponder which handles the asynchronous response.
* @see http://livedocs.adobe.com/flex/gumbo/langref/mx/rpc/AsyncToken.html
*
*/
public function addResponder(responder:IResponder) : void
{
token.addResponder( responder );
}
/**
*
* <code>RPCDiagnostics.executionComplete</code> is invoked upon an
* RPC <code>ResultEvent</code> or <code>FaultEvent</code> which is
* dispatched when the service returns a responder to the client. It
* is at this point that the duration of the operation is determined.
*
* @param <code>ResultEvent</code> or <code>FaultEvent</code>
*
*/
protected function executionComplete(value:Object) : void
{
execution.stop();
var elapsedTime:int = execution.getExecutionTotalDuration();
var message:String = StringUtil.substitute( _defaultMessage, operationName, elapsedTime );
trace( message );
execution = null;
token = null;
dispatchEvent( new RPCDiagnosticsEvent( operationName, elapsedTime ) );
}
/**
*
* @private
*
* <code>initializeResponders</code> ensures that the first instance
* of an <code>IResponder</code> in the <code>AsyncToken<code> is a
* reference to this object. If the <code>AsyncToken<code> instance
* contains previously added <code>IResponder</code> instances they
* are all promoted to an incrementing index.
*
*/
private function initializeResponders() : void
{
var responders:Array = token.responders;
var responder:IResponder = new Responder( executionComplete, executionComplete );
if ( responders != null )
{
responders.unshift( responder );
}
else
{
token.addResponder( responder );
}
}
/**
*
* <code>RPCDiagnostics.monitorToken</code> provides a convenience
* factory method from which an <code>AsyncToken</code> instance
* can be monitored.
*
* <p>
* This method is intended to be utilzed in order to support backwards
* compatability. Existing clients can utilize <code>RPCDiagnostics</code>
* with minimal change required to do so.
* </p>
*
* @example The following demonstrates an existing <code>AsyncToken</code>
* client which utilizes <code>RPCDiagnostics</code> to monitor the call.
*
* <listing version="3.0">
*
* var call:AsyncToken = RPCDiagnostics.monitorToken( service.send(), "operationName" );
* call.addResponder( this );
*
* </listing>
*
* @param The <code>AsyncToken</code> instance which is to be monitored
* @param The operation name which is being monitored
* @return Reference to the monitored <code>AsyncToken</code>
*
*/
public static function monitorToken(token:AsyncToken, operationName:String) : AsyncToken
{
var call:RPCDiagnostics = new RPCDiagnostics( token, operationName );
return token;
}
}
}