Chat Demo - Flash Client
Our discussion of the Java and C# clients emphasized the importance of maintaining a responsive user interface while performing network operations that have the potential to block. You might think that this issue is even more critical for a Flash client, given ActionScript's lack of support for threads. However, all of ActionScript's low-level network calls have non-blocking semantics, which fit quite nicely with Ice's Asynchronous Method Invocation (AMI) facility. In fact, when using the Ice language mapping for ActionScript, it is not possible to make synchronous invocations.
If you would like to see the full source code of the Flash client, please download one of the Ice for ActionScript distributions.
Creating a Session
One of the first tasks of a push client is establishing a session with the Glacier2 router. This process is greatly simplified by using the Glacier2 helper classes included with Ice. The client creates an instance of Glacier2.SessionFactoryHelper and passes it a callback object:
var callback:Glacier2.SessionCallback = ...;
var factory:Glacier2.SessionFactoryHelper =
new Glacier2.SessionFactoryHelper(initData, callback);
The callback is invoked to notify the application about significant events in the lifecycle
of a Glacier2 session.
Now we can use the factory to initiate the creation of a session:
factory.setRouterHost(...); factory.setPort(4502); factory.setSecure(false); _helper = factory.connect(userName, password);The client invokes setRouterHost to configure the name of the host on which the router is running. We have to use a TCP connection because Flash does not support SSL, so the client also configures the router's port and disables the use of SSL. Finally, the client invokes connect with the user-supplied account information.
The call to connect returns a new Glacier2.SessionHelper object that is responsible for managing the session and for invoking the client's SessionCallback object when necessary. For example, since the establishment of a session occurs asynchronously, the client cannot begin using the session until its connected callback method is invoked to indicate that the request completed successfully:
public function connected(session:Glacier2.SessionHelper):void
{
_session = Chat.ChatSessionPrxHelper.uncheckedCast(_helper.session());
_category = _helper.categoryForClient();
_helper.objectAdapter().whenCompleted(gotAdapter, ...);
}
The SessionHelper method session returns a proxy for the newly-created
Glacier2 session object. We down-cast this proxy to the derived interface
Chat::ChatSession that we saw earlier.
The client also retrieves the identity category that the router assigned to this session
and stores it away for later use, and then invokes the objectAdapter method
to obtain the object adapter that will host our callback object. Notice here that
the objectAdapter method has asynchronous semantics, and the code invokes
whenCompleted to register handlers for success and failure.
After the object adapter is created, the handler continues the process of preparing the callback object:
private function gotAdapter(ar:Ice.AsyncResult, adapter:Ice.ObjectAdapter):void
{
var callbackPrx:ChatRoomCallbackPrx =
ChatRoomCallbackPrxHelper.uncheckedCast(
adapter.add(new ChatRoomCallbackI(this),
new Ice.Identity(mx.utils.UIDUtil.createUID(),
_category)));
_session.setCallback(callbackPrx).whenCompleted(setCallbackCompleted, ...);
}
private function setCallbackCompleted(ar:Ice.AsyncResult):void
{
setStatus("connected");
}
This code creates the callback object that will receive events from the chat room. There's a lot happening in this short snippet of code. First, we instantiate our callback servant class ChatRoomCallbackI and pass it to the add method, along with an object identity consisting of a UUID for the identity name and the identity category that we saved earlier. The return value of add is a proxy that we narrow to the type Chat::ChatRoomCallback using an unchecked cast.
The last step is to join the chat room, which we accomplish by invoking setCallback on the session. This is another asynchronous invocation; upon completion, the handler updates the user interface to indicate that the session was established successfully.
Sending a Message
A new chat message is sent each time the user hits the Enter key in the text field. The MXML class for the main view defines the following event handler:
public function keyDownEventHandler(e:KeyboardEvent):void
{
if(e.keyCode == 13)
{
send(messageInput.text);
messageInput.text = "";
}
}
The send method uses the ChatSession proxy to deliver the
user's message to the chat room:
public function send(message:String):void
{
_session.send(message).whenCompleted(
function(r:Ice.AsyncResult, timestamp:Ice.Long):void
{
appendMessage(timestamp, _user, message);
},
...);
}
The code invokes the send operation and passes the message. The
return value of an asynchronous invocation is an Ice.AsyncResult
object, on which the code calls the whenCompleted method to
register its success and failure handlers. In this example, the success
handler is declared as an anonymous function whose only purpose is to
relay the time stamp, user name, and message text to the appendMessage
method:
public function appendMessage(timestamp:Ice.Long, user:String, message:String):void
{
var date:Date = new Date();
date.setTime(Number(timestamp.toString()));
messageList.dataProvider.addItem(
date.toLocaleTimeString() + " - <" + user + "> " + message);
scrollToBottom();
}
Here the code formats a message, appends it to the message log, and ensures
that the new message is visible.
If you would like to see the Flash client in action, you can use ZeroC's chat server.
| Previous: Applet Client |