Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (8.09 MB, 764 trang )
The example that follows uses two different types of AMFChannel: StreamingAMFChan
nel and AMFChannel. The StreamingAMFChannel provides support for messaging and offers a different push model than the base AMFChannel. Instead of polling the server, the
streaming channel opens a HTTP connection to the server that is held open so that data
can be streamed down to the client.
The AMFChannel can be configured to poll a server at an interval. You can also use this
channel with polling disabled to send RPC messages to remote destinations to invoke
their methods. The AMFChannel relies on network services that are native to the Flash
Player and AIR and are exposed to ActionScript by the NetConnection class. The channel
creates a new NetConnection for each instance. Channels are created within the framework using the ServerConfig.getChannel() method and can be constructed directly and
assigned to a ChannelSet instance. Channels represent a physical connection to a remote
endpoint and are shared across destinations by default. This means that a client targeting different destinations may use the same channel to communicate with these
destinations.
In polling mode, AMFChannel polls the server for new messages at the pollingInterval
property, 3 seconds by default. To enable polling, the channel must be connected and
the polling-enabled property in the configuration file must be set to true, or the poll
ingEnabled property of the channel must be set to true.
The complete code listing is as follows:
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo"
minWidth="1024" minHeight="768"
creationComplete=" initializeHandler()">
import spark.layouts.HorizontalLayout;
import spark.layouts.VerticalLayout;
]]>
import
import
import
import
mx.messaging.channels.StreamingAMFChannel;
mx.messaging.ChannelSet;
mx.messaging.channels.AMFChannel;
mx.messaging.events.MessageEvent;
15.6 Use Publish/Subscribe Messaging for Chat Applications | 421
www.it-ebooks.info
import mx.messaging.messages.AsyncMessage;
import mx.messaging.messages.IMessage;
Here, you create the AMFChannel and StreamingAMFChannel instances that will poll the
service for new messages:
private function initializeHandler():void
{
var myStreamingAMF:AMFChannel = new
StreamingAMFChannel("my-streaming-amf",
"pathToService/streamingamf");
var myPollingAMF:AMFChannel = new
AMFChannel("my-polling-amf", "pathToService/amfpolling");
myPollingAMF.pollingEnabled = true;
myPollingAMF.pollingInterval = 1000;
var channelSet:ChannelSet = new ChannelSet();
channelSet.addChannel(myStreamingAMF);
channelSet.addChannel(myPollingAMF);
You then use the new ChannelSet instance to configure the Consumer and Producer
instances:
}
consumer.channelSet = channelSet;
producer.channelSet = channelSet;
public function set room(name:String):void
{
if (!name) return;
}
if (consumer && consumer.subscribed)
{
chatLogTextArea.text += "Leaving room " +
consumer.destination + "\n";
consumer.unsubscribe();
}
consumer.destination = name;
producer.destination = name;
consumer.subscribe();
chatLogTextArea.text += "Entering room " + name + "\n";
private function send():void
{
var message:IMessage = new AsyncMessage();
message.body = msgTextInput.text;
producer.send(message);
msgTextInput.text = "";
}
private function messageHandler(event:MessageEvent):void {
}
chatLogTextArea.text += event.message.body + "\n";
422 | Chapter 15: Working with Services and Server-Side Communication
www.it-ebooks.info
Here, the messages are sent via the Producer instance:
private function sendMsg():void {
}
var message:IMessage = new AsyncMessage();
message.body.chatMessage = msgTextInput.text;
producer.send(message);
msgTextInput.text = "";
]]>
layout="{new VerticalLayout()}">
width="100%" height="100%"/>
In your service-config.xml file, you’ll need to define two channels for the service to use
(a StreamingAMFChannel and a PollingAMFChannel), like so:
class="mx.messaging.channels.StreamingAMFChannel">
class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
class="mx.messaging.channels.AMFChannel">
class="flex.messaging.endpoints.AMFEndpoint"/>
15.6 Use Publish/Subscribe Messaging for Chat Applications | 423
www.it-ebooks.info
In the messaging-config.xml file, note that the default channels have been configured
and that the destination is not declared. This is because SimpleChat will dynamically
create destinations to which the messages will be sent:
SimpleChat defines a getRooms() method available over remoting that returns all the
rooms that have been created. In a more robust chat application, this method would
be implemented using messaging rather than a remoting call so that all clients would
be notified when a new room is created.
Here is the code of remoting-config.xml:
The following is the code for a simple chat service (SimpleChat.java) for the BlazeDS
server. You might want to expand this service to allow users to dynamically add and
delete chat rooms or create private chat rooms, but for this recipe it suffices to show
how rooms can dynamically be created on the fly:
package oreilly.cookbook.flex4;
import
import
import
import
import
import
java.util.ArrayList;
java.util.Collections;
java.util.List;
flex.messaging.MessageBroker;
flex.messaging.MessageDestination;
flex.messaging.services.MessageService;
public class SimpleChat {
private List rooms;
private final String firstChatRoom = "firstChatRoom";
private final String secondChatRoom = "secondChatRoom";
private final String serviceName = "message-service";
private MessageDestination firstDestination;
private MessageDestination secondDestination;
public SimpleChat() {
rooms = Collections.synchronizedList(new ArrayList());
424 | Chapter 15: Working with Services and Server-Side Communication
www.it-ebooks.info
}
// note: not really the sleekest Java code, but has simplicity + readability
public List getRoomList() {
MessageBroker broker = MessageBroker.getMessageBroker(null);
MessageService service = (MessageService) broker.getService(serviceName);
if(firstDestination == null)
{
firstDestination = (MessageDestination)
service.getDestination(firstChatRoom);
if(service.getDestination(firstChatRoom) == null) {
firstDestination = (MessageDestination)
service.createDestination(firstChatRoom);
if (service.isStarted())
{
firstDestination.start();
}
}
rooms.add(firstChatRoom);
}
if(secondDestination == null)
{
secondDestination = (MessageDestination)
service.getDestination(secondChatRoom);
if(secondDestination == null) {
secondDestination = (MessageDestination)
service.createDestination(secondChatRoom);
if (service.isStarted())
{
secondDestination.start();
}
}
rooms.add(secondChatRoom);
}
}
}
return rooms;
You’ll also need to make sure that you set the following compiler arguments:
-locale en_US -services={$BLAZE_ROOT}/webapps/samples/WEB-INF/flex/services-config.xml
To see how to fully configure the services-config.xml file, refer back to the two previous
recipes.
See Also
Figures 15.4 and 15.5
15.6 Use Publish/Subscribe Messaging for Chat Applications | 425
www.it-ebooks.info
15.7 Use the IExternalizable Interface for Custom Serialization
Contributed by Peter Farland
Problem
You want to customize which properties are sent over the wire when sending strongly
typed data via a RemoteObject or DataService component.
Solution
Use the ActionScript 3 API flash.utils.IExternalizable, which is compatible with
Java’s java.io.IExternalizable API.
Discussion
A common scenario for using externalizable classes is to include read-only properties
in serialization. Although there are other approaches to achieve this for server code,
there aren’t many approaches available for client code. So, for an elegant solution that
works for both the client and the server, you can make your classes externalizable for
two-way custom serialization.
This approach is relatively straightforward. The client ActionScript class simply
implements flash.utils.IExternalizable. This API requires two methods,
readExternal() and writeExternal(), which take flash.utils.IDataInput and
flash.utils.IDataOutput streams, respectively. The implementations of these methods
mirror the server Java class, which implements java.io.Externalizable. java.io.Exter
nalizable also has readExternal() and writeExternal() methods that take
java.io.ObjectInput and java.io.ObjectOutput streams, respectively.
Although the IDataInput and IDataOutput classes let you design your own protocol and
write such fundamental data types as byte, int, and UTF-8-encoded Strings, most
implementations take advantage of the readObject() and writeObject() methods,
respectively, as these use AMF 3 to efficiently deserialize and serialize ActionScript
objects. (Remember that AMF 3 has three advantages. First, you can send objects by
reference to avoid redundant instances from being serialized, to retain object relationships, and to handle cyclical references. Second, you can send object traits so that the
description of a type is sent only once rather than repeated for each instance. Finally,
you can send reoccurring strings by reference to again avoid redundant information
being sent.) You may even decide to omit property names altogether in your externalizable classes’ custom serialization code and rely on a fixed order to send just the
property values.
426 | Chapter 15: Working with Services and Server-Side Communication
www.it-ebooks.info
This example focuses on serializing read-only properties, but there may
be many other usages for custom serialization, such as omitting properties, avoiding redundant serialization of information, or including
properties from custom namespaces.
Notice how the following Java writeExternal() method:
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeObject(id);
out.writeObject(name);
out.writeObject(description);
out.writeInt(price);
}
mirrors the client readExternal() method in ActionScript:
public function readExternal(input:IDataInput):void
{
_id = input.readObject() as String;
name = input.readObject() as String;
description = input.readObject() as String;
price = input.readInt();
}
A similar relationship exists for the reverse situation, sending instances back from the
client to the server.
15.8 Track Results from Multiple Simultaneous Service Calls
Contributed by Andrew Alderson
Problem
You need to determine what returned data belongs to which of your multiple simultaneous service calls.
Solution
Use ASyncToken to add a variable to each call to identify it.
Discussion
Because mx.rpc.ASyncToken is a dynamic class, you can add properties and methods to
it at runtime. The Flex documentation states that it is “a place to set additional or tokenlevel data for asynchronous rpc operations.”
15.8 Track Results from Multiple Simultaneous Service Calls | 427
www.it-ebooks.info