Listing 1 serverRequest.cfm

<!---
	This module accepts all incoming requests from the Flash client.

	First the user is authenticated (if required). If authenticated, then the
	requested
	function is invoked, and the results returned to the Flash client.

	If the user is not authenticated, then we return an error code (-1),
	along with the parameters supplied. This will enable the client
	to resubmit the function call (if required) after requesting a
	new password from the user.
 --->

<cftry>
	<cfinvoke component="clientAuth" method="authenticate"
	returnvariable="authenticated">
		<cfinvokeargument name="username" value="#flash.params[1].username#">
		<cfinvokeargument name="password" value="#flash.params[1].password#">
	</cfinvoke>

	<cfif authenticated>
		<cfinvoke component="#flash.params[1].component#"
		method="#flash.params[1].function#"
		returnvariable="flash.result"
		argumentcollection="#flash.params[1].params#"/>
	<cfelse>
		<cfscript>
			flash.result			= structNew();
			flash.result.status  	= arguments.status;
			flash.result.result  	= arguments.result;
		</cfscript>
	</cfif>

	<!--- Pass back module and method so that the Flash client knows which
	message
	response this is --->
	<cfset flash.result.component = flash.params[1].component>
	<cfset flash.result.function  = flash.params[1].function>

	<cfcatch>
		<cfscript>
			if (isDefined("Flash.params"))
			{
				flash.result			= structNew();
				flash.result.status  	= arguments.status;
				flash.result.result  	= arguments.result;

				flash.result.serverTime = timer.time;
				flash.result.messageID = flash.params[1].messageID;

				if (flash.result.status LT 1)
					flash.result.params = flash.params[1].params;
			}
			else
		//Someone has tried to access this gateway without a Flash client
				writeOutput("THIS IS A FLASH GATEWAY ONLY");
		</cfscript>
	</cfcatch>
</cftry>


Listing 2 RemotingSetup.as #include "NetServices.as"

var gatewayOpen = false;

function openFlashRemotingGateway()

// Opens a connection to the Flash Remoting Server
// gatewayOpen variable is used to ensure that this code is only run once.

{
	NetServices.setDefaultGatewayUrl("http://localhost:8500/flashservices/gateway");
	gatewayConnnection 	= NetServices.createGatewayConnection();
	ld4uService   		= gatewayConnnection.getService("client", this);
	gatewayOpen 		= true;
}

function serverRequest(params)

// Prepares a message request and sends it to the Flash Remoting server
//
// A call is then made to the serverRequest function that handles all
// remoting requests from the flash client

{
	trace("_root : Sending Remoting Request | COMPONENT : " + params.component + "
	| FUNCTION : " + params.function + " | PATH : " + params.path);

	if (!gatewayOpen) openFlashRemotingGateway();

	params.username  = _global.username
	params.password  = _global.password

	ld4uService.serverRequest(message);

}


function serverRequest_Result(result)

// Handle receipt of replies from Flash Remoting server
// Handles receipt of authentication error by requesting user to login again
//
// Routes call to the correct movie clip by utilising the PATH contained
// in the results returned from the server
//
// Calls a function who's name is <COMPONENT>_<FUNCTION>_Result, and passes
// the function calls results without the wrapper data that was used
// by this function (ie. passes 'result.result' rather than 'result'

{
	switch (result.status)
	{
		case -1:
			trace("E R R O R : Authentication error - need to login again");
			break;

		case "0":
			trace("E R R O R : " + result.result);
			targetFunction  = result.component + "_" + result.function +
			"_Failure";
			this[targetFunction](result.result);
			break;

		case "1":
			trace("R E M O T I N G : Flash remoting call returned a success
			message");
			targetFunction  = result.component + "_" + result.function + "_Result";
			this[targetFunction](result.result);
			break;
	}
}



function serverRequest_Status(result)
{
	// This should never occur
	trace("_root.Remoting Exception : " + result)
}

function coursePlayer_getPageTemplate_Result(result)
{
	trace("coursePlayer_getPageTemplate_Result called")
}

Listing 3 serverRequest.cfm

<!--- 
	This module accepts all incoming requests from the Flash client.

	First the message time is checked. We only accept messages that arrive
	within a certain time. This is to prevent someone capturing a valid message
	and sending it to us multiple times. We always returnt the current server
	time to the client, so that the clients can adjust their timestamps if
	their time is different from the server time.

	Next the user is authenticated (if required). If authenticated, then the
	requested
	function is invoked, and the results returned to the Flash client.

	If the user is not authenticated, then we return an error code (-1),
	along with the parameters supplied. This will enable the client
	to resubmit the function call (if required) after requesting a
	new password from the user.

	In both cases, we return the module and method names to the Flash client
	in order for it to determine what the results returned relate to.

	The flash client passes a PATH parameter to allow the outer SWF to route the
	results back to the correct embedded movie.
 --->

 <!---
 	Some stuff to help us debug - only active if application.debug is TRUE
 	The debug call at the end will ovewrite this, but if something disasterous
	happens, and we don't get to the end of this code, at least we can see
	what was passed into this code from Flash.
 --->
<cfset do_debug = true>

<CFIF do_debug>
 	<cfsavecontent variable="cfdump1"><cfdump
	var="#flash#"></cfsavecontent>
 	<cffile action="WRITE" file="#expandPath('.')#/cfdump.html"  addnewline="No"
	output="INPUT:<BR>#cfdump1#">
</CFIF>

<cftry>
	<cfinvoke component="clientAuth" method="authenticate"
	returnvariable="authenticated">
		<cfinvokeargument name="params" value="#flash.params[1]#">
	</cfinvoke>

	<cfif authenticated>
		<cfif NOT isStruct(flash.params[1].params)>
			<cfset flash.params[1].params = structNew()>
		</cfif>
		<cfinvoke component="#flash.params[1].component#"
		method="#flash.params[1].function#"
		returnvariable="flash.result"
		argumentcollection="#flash.params[1].params#"/>
		<cfset PrepareReplyMessage()>
	<cfelse>
		<cfset PrepareErrorMessage(-1,"Your username/password have not been
		accepted")>
	</cfif>

	<cfcatch>
		<cfscript>
			if (isDefined("Flash.params"))
				PrepareErrorMessage(0,cfcatch.message);
			else
		//Someone has tried to access this gateway without a Flash client
				writeOutput("UNAUTHORISED ACCESS METHOD");
		</cfscript>
	</cfcatch>
</cftry>

<!---
	Some stuff to help us debug - only active if application.debug is TRUE
	We are dumping out a compelete copy of the message sent to us by Flash,
	and a copy of the message that we are returning. For good measure, we
	are also dumping the request scope variables that we are using frequently
	in our code.
--->
<cftry>
	<CFIF do_debug>
	 	<cfsavecontent variable="cfdump2"><cfdump var="#flash#"><cfdump
		var="#request#"></cfsavecontent>
		<cfset dumpTime = Now()>
	 	<cfif NOT directoryExists("#expandPath('.')#/debugDumps")>
			<cfdirectory action="CREATE"
			directory="#expandPath('.')#/debugDumps">
		</cfif>
		<cfset dumpFileName =
		"#flash.params[1].module#_#flash.params[1].method#_#GetTickCount()#.html">
		<cffile action="WRITE"
		file="#expandPath('.')#/debugDumps/#dumpFileName#"
		addnewline="No"
		output="INPUT:<BR>#cfdump1#<P>OUTPUT:<P>#cfdump2#">
	</CFIF>
	<cfcatch>
	 	<cfsavecontent variable="cfdump3"><cfdump
		var="#cfcatch#"></cfsavecontent>
	 	<cffile action="WRITE" file="#expandPath('.')#/cfdump.html"
		addnewline="No" output="INPUT:<BR>#cfdump1#<P>ERROR:<P>#cfdump3#">
	</cfcatch>
</cftry>


<!--- Some functions to prepare our reply message --->
<cffunction name="PrepareErrorMessage" access="private">
	<cfargument name="status" type="numeric" required="yes">
	<cfargument name="result" type="string"  required="yes">

	<cfscript>
		flash.result			= structNew();
		flash.result.status  	= arguments.status;
		flash.result.result  	= arguments.result;

		PrepareReplyMessage();

		// for debugging
		if (application.debug AND isDefined("cfcatch"))
			flash.result.cfcatch 	= cfcatch;
	</cfscript>

</cffunction>

<cffunction name="PrepareReplyMessage" access="private">

	<cfscript>
		// Send difference between client tiem and server time
		flash.result.serverTime = timer.time;

		// Pass message ID back to flash
		flash.result.messageID = flash.params[1].messageID;

		// If the call was unsucessfull for any reason, then we
		// send back the parameters sent to us so that the client
		// can automatically resubmit the function call once the
		// error has been corrected.

		if (flash.result.status LT 1)
			flash.result.params = flash.params[1].params;

	</cfscript>

</cffunction>

Listing 4 remotingSetup.as

#include "NetServices.as"
#include "common.as\Authentication.as"
#include "common.as\userDetails.as"
#include "NetDebug.as"

var gatewayOpen = false;

var timeoutPeriod = 20000

// Set server time snapshot
var localServerTimeDiff = 0

function openFlashRemotingGateway()

// Opens a connection to the Flash Remoting Server
// gatewayOpen variable is used to ensure that this code is only run once.

{
	NetServices.setDefaultGatewayUrl("http://localhost:8500/flashservices/gateway");
	gatewayConnnection 	= NetServices.createGatewayConnection();
	ld4uService   		= gatewayConnnection.getService("client", this);
	gatewayOpen 		= true;
}

function openSecureFlashRemotingGateway()

// Opens a connection to the Flash Remoting Server
// gatewayOpen variable is used to ensure that this code is only run once.

{
	secureGatewayConnnection 	=
	NetServices.createGatewayConnection("https://localhost:8500/flashservices/gate
	way");
	secureld4uService   		= secureGatewayConnnection.getService("client",
	this);
	secureGatewayOpen 			= true;
}


// Used to record messages sent to the server
var messageID = 1
var sentMessages   = new Array()

// Used to hold messages that have failed authentication, or have timedout,
// and are awaiting resubmission.

var queuedMessages = new Array()


function clearMessageQueue()
{
	trace("_root.clearMessageQueue")
	while(queuedMessages.length)
	{
		messageData = queuedMessages.pop()
		trace("Sending failure for component =  " + messageData.component + " :
		function = " + messageData.function)
		
		targetMovieClip = getTargetPath(messageData.path)
		targetFunction  = messageData.component + "_" + messageData.function +
		"_Failure";
		
		trace("_root : target movie clip = " + targetMovieClip);
		trace("_root : target function clip = " + targetFunction);

		if (messageData.secure)
			reason = "Authentication failure"
		else
			reason = "Message timeout, server not responding"

		targetMovieClip[targetFunction](reason,messageData.passthrough);
	}
}


function resendQueuedMessages()
{
	trace("_root.resendQueuedMessages : There are " + queuedMessages.length + "
	messages queued")

	while(queuedMessages.length)
	{
		myMessage = queuedMessages.pop()
		trace("_root.resendQueuedMessages : Resending component =  " +
		myMessage.component + " : function = " + myMessage.function)

		flash = new Array()
		flash.component 		= myMessage.component
		flash.function 			= myMessage.function
		flash.path 	 			= myMessage.path
		flash.passthrough 		= myMessage.passthrough
		flash.params 			= myMessage.params
		if (myMessage.secure)
			_root.encryptedServerRequest(flash);
		else
			_root.serverRequest(flash);
	}
}




function RemotingMessage(params,messageID,secure)
{
	trace("_root.remoting_message : Created new message ID " + messageID)

	this.component		= params.component
	this.function		= params.function
	this.path			= params.path
	this.params			= params.params
	this.passthrough	= params.passthrough
	timeNow 			= new Date()
	this.clientTime		= timeNow.getTime()
	this.timerID		= setInterval(messageTimeout,timeoutPeriod,messageID)
	this.secure			= secure
}


function getTargetPath(path)
{
	targetMovieClip = this;
	targetPath = path.split(".")

	for (i=0;i<targetPath.length;i++)
		targetMovieClip = targetMovieClip[targetPath [i]];

	return targetMovieClip
}



function messageTimeout(messageID)
{
	// get message data and delete entry from sentMessages, and clear timer
	messageData = _root["sentMessages.message_" + messageID]

	// Add failed message to queue
	queuedMessages.push(messageData)

	// need to keep this since we may be resending lots of messages
	delete _root["sentMessages.message_" + messageID]

	clearInterval(messageData.timerID)

	trace("_root.messageTimeout message ID " + messageID + " has timed out")
	trace("_root.messageTimeout " + messageData.component + ":" +
	messageData.function)

	// Warn user about unresponsive server
	// timeout movie clip will

	main_mc.createEmptyMovieClip("timeoutWarning", 100);
	main_mc.timeoutWarning.loadMovie("pc/welcome/timeout.swf");
}




function serverRequest(params)

// Prepares a message request and sends it to the Flash Remoting server
//
// Username is appended to the flash request data
// Password is used to generate a secure hash (call to authenticate function)
// that is also appended to the flash request data
//
// A call is then made to the serverRequest function that handles all
// remoting requests from the flash client

{
	trace("_root : Sending Remoting Request | component : " + params.component + "
	| function : " + params.function + " | PATH : " + params.path);

	if (!gatewayOpen) openFlashRemotingGateway();

	var message = prepareRemotingMessage(params,false)

	ld4uService.serverRequest(message);

}


function prepareRemotingMessage(params,secure)
{
	var thisMessageID = messageID++

	params.username  = _global.username
	params.messageID = thisMessageID

	_root["sentMessages.message_" + thisMessageID] = new
	RemotingMessage(params,thisMessageID,secure)

	path 		= params.path
	passthrough = params.passthrough

	delete params.path
	delete params.passthrough

	params.authenticate = authenticate(_global.password,params);

	return params
}


function serverRequest_Result(result)

// Handle receipt of replies from Flash Remoting server
// Handles receipt of authentication error by requesting user to login again
//
// Routes call to the correct movie clip by utilising the PATH contained
// in the results returned from the server
//
// Calls a function who's name is <component>_<function>_Result, and passes
// the function calls results without the wrapper data that was used
// by this function (ie. passes 'result.result' rather than 'result'

{
	serverRequestResult = result; // For debugging only
	
	trace("_root.serverRequest_Result : received message id = " + result.messageID)

	messageData = _root["sentMessages.message_" + result.messageID]
	delete _root["sentMessages.message_" + messageID]
	clearInterval(messageData.timerID)


	if (messageData.component != undefined)
	{
		trace("_root : Flash remoting response received for component : " +
		messageData.component + " | function : " + messageData.function + " |
		STATUS : " + result.status + " | MOVIE CLIP PATH : " + result.path);

		localServerTimeDiff = result.serverTime - messageData.clientTime

		switch (result.status)
		{
			case -1:
				trace("E R R O R : Authentication error - need to login again");
				// Add message to queue for resending
				queuedMessages.push(messageData)

				_root.setArea = "other";
				main_mc.createEmptyMovieClip("login", 100);
				main_mc.login.loadMovie("login.swf");

				break;

			case -2:
				trace("E R R O R : Timeout error. Message took too long,
				re-submit");

				flash = new Array();
				flash.component 	= messageData.component;
				flash.function 		= messageData.function;
				flash.path 			= messageData.path;
				flash.params 		= messageData.params;
				flash.passthrough 	= messageData.passthrough;
				_root.serverRequest(flash);

				break;

				
			case "0":
				trace("E R R O R : " + result.result);

				targetMovieClip = getTargetPath(messageData.path)

				targetFunction  = messageData.component + "_" +
				messageData.function + "_Failure";

				trace("_root : target movie clip = " + targetMovieClip);
				trace("_root : target function clip = " + targetFunction);
	targetMovieClip[targetFunction](result.result,messageData.passthrough);
				break;

			
			
			case "1":
				trace("R E M O T I N G : Flash remoting call returned a
				successmessage");

				targetMovieClip = getTargetPath(messageData.path)

				targetFunction  = messageData.component + "_" +
				messageData.function + "_Result";

				trace("_root : target movie clip = " + targetMovieClip);
				trace("_root : target function clip = " + targetFunction);

				targetMovieClip[targetFunction](result.result,messageData.passthrough);
				break;
		}
	}
	else
	{
		// discard message because it has timedout and we have already sent a
		failure message
		trace("_root : Remoting response received, and discarded due to previous
		timeout")
	}
}



function serverRequest_Status(result)
{
	// This should never occur
	trace("_root.Remoting Exception : " + result)
}