Flex Data Services 2 简介

What are Flex Data Services 2?

FDS 2 is a web application, not a server plugin. It also provides both server-side and client-side library. It can deploy on webshere, tomcat, jrun, etc.

New features and changes for Flex Data Services 2:

Data synchronization between client and server, Publish/subscribe messaging, Data paging, Data push, In-context collaboration(Data synchronization between clients).

See Flex 2.0.1 release notes, it includes details about new features, how to integrate log4j, etc. ( http://www.adobe.com/support/documentation/en/flex/2/releasenotes_flex2_fds.html)

Channels

Channels are communication method between flex clients (flash player, etc.) and FDS, which is transparent to developers.

There are three basic channels: http, amf, rtmp. Amf is based on http, can be configured with polling, to simulate rtmp. All channels can be configured in secure way. So there are really eight channels: http, http-secure, amf, amf-secure, amf-polling, amf-secure-polling, rtmp, rtmp-secure.

Each kind of flex data services can use some or all channels above. Which channel it uses is XML-based configurable, so developers don’t need to worry about it when they are coding. But they should know only rtmp is really real-time, which uses non-standard http port which may be blocked by firewall. Others all use http port, and amf-polling can simulate push but with a configurable delay.

RTMP

http://www.cnbruce.com/blog/showlog.asp?cat_id=24&log_id=828

http://www.nettime.net.cn/itedu/news/2005128/2005128112617790.htm

http://www.nshen.net/blog/default.asp?cat=16

http://www.blueidea.com/computer/net/2005/2778.asp

RTMP(the Real-time Messaging Protocol)协议作为客户端和服务器端的传输协议,这是一个专门为高效传输视频、音频和数据而设计的 TCP/IP 协议,使用 RTMP 协议传输的数据是未经加密的,包括用户名和密码等认证信息。(http://www.javvin.net/ProtocolsTerms/RTMP2.php)

(http://ria.richtechmedia.com/2006/06/21/flex-2-%E6%87%89%E7%94%A8%E8%AA%AA%E6%98%8E%E8%A8%AA%E5%95%8F/)In Flex we actually support two protocols. There is HTTP and RTMP. The HTTP of course now is a one-way protocol where the client always initiates the call so for that we do use a polling mechanism. With RTMP, it’s a protocol that has been used by the Flash Player for quite a while to do audio, video and data synchronization and it provides a persistent socket that the client keeps open with the server, to analyze the server it just naturally push messages to the client in real time. Flex Data Services provides an abstraction layer, so the client does not really need to know, which protocol is he using and it actually fails over from one to the other, so that technique actually makes it easy to build applications that support push even when the user might be applying a firewall and can’t use RTMP.

(http://livedocs.adobe.com/flex/201/html/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Book_Parts&file=security2_117_01.html#137544)Flash Player uses the Real-Time Messaging Protocol (RTMP) for client-server communication. This is a TCP/IP protocol designed for high-performance transmission of audio, video, and data messages. RTMP sends unencrypted data, including authentication information (such as a name and a password). Although RTMP in and of itself does not offer security features, Flash communications applications can perform secure transactions and secure authentication through an SSL-enabled web server. Flash Player also provides support for versions of RTMP that are tunneled through HTTP and HTTPS. RTMP refers to RTMP transmitted within an HTTP wrapper, and RTMPS is RTMP transmitted within an HTTPS wrapper.

RTMP remains a permanent connection between client and server, so the maximum number of connections is limited. 我小测了一下, 在Websphere test environment中使用RTMP, 没有Workmanage只支持9个连接, 第十个一连就抛异常了:

J2CA0020E: The Connection Pool Manager could not allocate a Managed Connection: com.ibm.websphere.ce.j2c.ConnectionWaitTimeoutException: Connection not available

Three flex server data access features

RPC (Remote Process Call) services. Asynchronous. 包括HTTPService – HTTP URL, WebService – a SOAP-compliant web service, RemoteObject – POJO (Plain Old Java Object), 其中前两者可以不需要FDS的支持, 只要Flex SDK 2就够了, 因为这两者可以直接通过URL来进行相应的调用, 但这样就无法利用FDS2提供的服务, 如Proxy等等. 而RemoteObject需要FDS才能使用. Flex HTTPService与AJAX中的xmlhttp类似. WebService支持调用SOAP-based web services. RemoteObject能直接远程调用server端的java objects. 此三者都是通过Http端口进行数据传输 (RO使用AMF协议是基于HTTP协议的), 都是客户端发起.

Data management service. 需FDS支持. 支持Clients和Server数据同步, 支持分布式数据, 支持push.

Message service. 需FDS支持. 支持server和clients的异步通讯. 支持JMS provider等FDS之后的message provider.

从client-side代码来看, 所有这些方式都是类似的, 都是通过destination访问, 而不是URL. 因为在FDS2中, 这些services都通过XML配置文件定义唯一的destination.

RPC services

Data management service

Adapter

Used for server-side additional data operation when automatically data synchronization happens.

ActionScript adapter -- persists data in server memory and is useful for applications that require transient distributed data that is not persisted to a data store.
Used for automatic clients synchronization, not server push.

Java adapter -- passes data changes to methods available on an arbitrary Java class, referred to as the Java assembler. This adapter lets Java developers employ the Data Transfer Object (DTO) design pattern.
Automatic clients synchronization, also server push with flex Server-Side API.
Developers implement java assembler to do custom action for every data synchronization, like data persistent, etc.
Server-side java code can begin a data synchronization use flex server-side API, which is really a server push.

DMS在server端做的是根据某一个client的更新, 更新自身数据, 同时更新其他clients数据, 如果采用Java adapter则可以进行持久化. 更新其他clients数据对developer来说几乎是透明的, 而持久化则需要developer自己显式实现. 所以实际上server内存中的数据起到一个枢纽作用. FDS与Clients的数据同步可自动完成, 但需要手动把数据持久化.

每一次client更新通知server, 传到server的是一个更新列表, 包括create, update, delete, 每个更新元素保存在flex.data.ChangeObject中, 可用isCreate(), isUpdate(), isDelete()进行判断, 并通过getNewVersion()和getPreviousVersion()得到完整的数据元素. 每个更新元素有一个identity值, 就是用来标志是哪一个元素, 这在 <metadata></metadata> 中设定. 客户端传给server的每个ChangeObject中都包含这个值, 并来标志该元素, 以及指定各个客户端将被动态更新的元素.

可以有两种方式来在Server端Java代码中对客户端和服务端数据同步时做一些自己的操作, 比如数据持久化. 其一是Using the fill-method and sync-method approach, 就是在XML文件中配置fill和synchronize的方法; 另一种是Using the Assembler interface approach, 只要自己定义的Assembler继承类flex.data.assemblers.AbstractAssembler 即可. Assembler是指在XML配置的能与Java Adapter交互的类.

“You can implement a Data Management Services Assembler by extending this AbstractAssembler class. When you extend this class, you do not need to specify the methods via the XML tags in the Data Services configuration (if you also specify the XML tags, the methods specified in the XML tags will be used instead of the methods in this interface).”

当某一个客户端数据变化, 或者通用Server API (flex.data.DataServiceTransaction) 调用都可以同步各个客户端与server的数据. 也就是说, 使用flex.data.DataServiceTransaction 是可以实现server data push的. An instance of the DataServiceTransaction class is created for each operation that modifies the state of objects that the Data Management Service manages.

It also support paging feature.

Difficult to push specific data to a specific user??

DMS可以配置使用rtmp或amf polling (both secure or not) channel.

上文提到的两种方法共有的代码如下:

Server-side (java codes)

WebContent/WEB-INF/flex/remoting-config.xml 中相关配置

< destination  id ="tony_contact" >
        
< properties >
            
< source > tony.contact.ContactRO </ source >
            
< scope > application </ scope >
        
</ properties >
    
</ destination >

 

ContactVO.java

/**
 * ContactVO.java
*/


package  tony.contact;

/**
 * 
@author Tony
 
*/

public   class  ContactVO
{
    
private int contactId;
    
private String firstName;
    
private String lastName;
    
    
public int getContactId()
    
{
        
return contactId;
    }


    
public void setContactId(int contactId)
    
{
        
this.contactId = contactId;
    }


    
public String getFirstName()
    
{
        
return firstName;
    }


    
public void setFirstName(String firstName)
    
{
        
this.firstName = firstName;
    }


    
public String getLastName()
    
{
        
return lastName;
    }


    
public void setLastName(String lastName)
    
{
        
this.lastName = lastName;
    }

    
    
public ContactVO()
    
{
    }

    
    
public ContactVO(int contactId, String firstName, String lastName)
    
{
        
this.contactId = contactId;
        
this.firstName = firstName;
        
this.lastName = lastName;
    }

}

 

ContactDAO.java

/**
 * ContactDAO.java
*/


package  tony.contact;

import  java.util.List;
import  flex.messaging.io.ArrayList;

/**
 * 
@author Tony
 
*/

public   class  ContactDAO
{
    
static List myData = null;
    
static int count = 10;
    
static
    
{
        myData 
= new ArrayList();
        ContactVO cvo;
        
for (int i=0; i<count; i++)
        
{
            cvo 
= new ContactVO();
            cvo.setContactId(i);
            cvo.setFirstName(
"firstName" + i);
            cvo.setLastName(
"lastName" + i);
            myData.add(cvo);
        }

    }

    
    
public List getContacts()
    
{
        
return myData;
    }

    
    
public List getContacts(String name)
    
{
        
return getContacts();
    }

    
    
public ContactVO create(ContactVO contact)
    
{
        contact.setContactId(count
++);
        myData.add(contact);
        
return contact;
    }

    
    
public void update(ContactVO newContact, ContactVO preContact)
    
{
        myData.set(preContact.getContactId(), newContact);
    }

    
    
public void delete(ContactVO contact)
    
{
        myData.remove(contact.getContactId());
    }

}

 

ContactRO.java

/**
 * ContactRO.java
*/


package  tony.contact;

import  flex.data.DataServiceTransaction;

/**
 * 
@author Tony
 
*/

public   class  ContactRO
{
    
public void modDataByServerPush()
    
{
        DataServiceTransaction dtx 
= DataServiceTransaction.begin(false);
        
try
        
{
            ContactVO ct 
= new ContactVO(1"test""serverAPI");
            dtx.updateItem(
"tony.dms.contact", ct, nullnull);
            
new ContactDAO().update(ct, ct);
        }

        
finally
        
{
            dtx.commit();
        }

    }

}

Client-side (AS, mxml codes)

ContactVO.as

//  ContactVO.as

package tony.contact
{
    [Managed]
    [RemoteClass(alias
="tony.contact.ContactVO")]
    public class ContactVO
    
{
        public 
var contactId:int;
        public 
var firstName:String = "";
        public 
var lastName:String = "";
    }

}

 

contact.mxml

<? xml version="1.0" encoding="utf-8" ?>
< mx:Application  xmlns:mx ="http://www.adobe.com/2006/mxml"
    creationComplete
="initApp();" >
    
< mx:Script >
        
<![CDATA[
            import mx.data.DataService;
            import mx.collections.ArrayCollection;
            import tony.contact.ContactVO;
            
            public var ds:DataService;
            [Bindable]
            public var contacts:ArrayCollection;
            private var cont:ContactVO;
            
            public function initApp():void
            {
                contacts = new ArrayCollection();
                ds = new DataService("tony.dms.contact");
                ds.fill(contacts);
            }
        
]]>
    
</ mx:Script >
    
    
< mx:RemoteObject  id ="contactRO"  destination ="tony_contact"  
           showBusyCursor
="false" />
           
    
< mx:DataGrid  id ="dg"  dataProvider ="{contacts}"  editable ="true" >
        
< mx:columns >
            
< mx:DataGridColumn  dataField ="contactId"  headerText ="Id" />
            
< mx:DataGridColumn  dataField ="firstName"  headerText ="First Name" />
            
< mx:DataGridColumn  dataField ="lastName"  headerText ="Last Name" />
        
</ mx:columns >
    
</ mx:DataGrid >
    
< mx:HBox  width ="{dg.width}" >
        
< mx:Button  label ="DataService.fill"  click ="ds.fill(contacts)" />  
        
< mx:Button  label ="Show Server Push API"  click ="contactRO.modDataByServerPush()" />
    
</ mx:HBox >
    
</ mx:Application >

不同的代码

Server-side

First one:

WebContent/WEB-INF/flex/data-management-config.xml 中相关配置

     < destination  id ="tony.dms.contact" >
        
< adapter  ref ="java-dao" />
        
< properties >
            
< use-transactions > false </ use-transactions >
        
            
< source > tony.contact.ContactAssembler </ source >
            
< scope > application </ scope >
            
< metadata >
                
< identity  property ="contactId" />
            
</ metadata >
            
< network >
                
< session-timeout > 20 </ session-timeout >
                
< paging  enabled ="false"  pageSize ="10" />
                
< throttle-inbound  policy ="ERROR"  max-frequency ="500" />
                
< throttle-outbound  policy ="REPLACE"  max-frequency ="500" />
            
</ network >
            
< server >
                
< fill-method >
                    
< name > loadContacts </ name >
                
</ fill-method >
                
< fill-method >
                    
< name > loadContacts </ name >
                    
< params > java.lang.String </ params >
                
</ fill-method >
                
< sync-method >
                    
< name > syncContacts </ name >
                
</ sync-method >
            
</ server >
        
</ properties >
        
< channels >
            
< channel  ref ="my-rtmp" />
        
</ channels >
    
</ destination >

 

ContactAssembler.java

/**
 * ContactAssembler.java
*/


package  tony.contact;

import  java.util.Iterator;
import  java.util.List;
import  flex.data.ChangeObject;

/**
 * 
@author Tony
 
*/

public   class  ContactAssembler
{
    
public List loadContacts()
    
{
        ContactDAO dao 
= new ContactDAO();
        
return dao.getContacts();
    }

    
    
public List loadContacts(String name)
    
{
        ContactDAO dao 
= new ContactDAO();
        
return dao.getContacts(name);
    }

    
    
public List syncContacts(List changes)
    
{
        Iterator it 
= changes.iterator();
        ChangeObject co;
        
while (it.hasNext())
        
{
            co 
= (ChangeObject)it.next();
            
if (co.isCreate())
            
{
                co 
= doCreate(co);
            }

            
else if (co.isUpdate())
            
{
                doUpdate(co);
            }

            
else if (co.isDelete())
            
{
                doDelete(co);
            }

        }

        
return changes;
    }


    
private void doDelete(ChangeObject co)
    
{
        ContactDAO dao 
= new ContactDAO();
        dao.delete((ContactVO)co.getPreviousVersion());
    }


    
private void doUpdate(ChangeObject co)
    
{
        ContactDAO dao 
= new ContactDAO();
        
//Contact cont = (Contact)co.getNewVersion();
        
//cont.setFirstName("ZhuA");
        dao.update((ContactVO)co.getNewVersion(), (ContactVO)co.getPreviousVersion());
    }


    
private ChangeObject doCreate(ChangeObject co)
    
{
        ContactDAO dao 
= new ContactDAO();
        ContactVO contact 
= dao.create((ContactVO)co.getNewVersion());
        co.setNewVersion(contact);
        
return co;
    }

}

 

Second one:

WebContent/WEB-INF/flex/data-management-config.xml 中相关配置

     < destination  id ="tony.dms.contact" >
        
< adapter  ref ="java-dao" />
        
< properties >
            
< use-transactions > false </ use-transactions >

            
< source > tony.contact.AnotherContactAssembler </ source >
            
< scope > application </ scope >
            
< metadata >
                
< identity  property ="contactId" />
            
</ metadata >
            
< network >
                
< session-timeout > 20 </ session-timeout >
                
< paging  enabled ="false"  pageSize ="10" />
                
< throttle-inbound  policy ="ERROR"  max-frequency ="500" />
                
< throttle-outbound  policy ="REPLACE"  max-frequency ="500" />
            
</ network >
        
</ properties >
        
< channels >
            
< channel  ref ="my-rtmp" />
        
</ channels >
    
</ destination >

 

AnotherContactAssembler.java

/**
 * AnotherContactAssembler.java
*/


package  tony.contact;

import  java.util.Collection;
import  java.util.List;
import  flex.data.assemblers.AbstractAssembler;

/**
 * 
@author Tony
 
*/

public   class  AnotherContactAssembler  extends  AbstractAssembler
{
    
public void createItem(Object newItem)
    
{
        
new ContactDAO().create((ContactVO)newItem);
    }

    
    
public void deleteItem(Object preItem)
    
{
        
new ContactDAO().delete((ContactVO)preItem);
    }

    
    
public Collection fill(List fillParameters)
    
{
        ContactDAO dao 
= new ContactDAO();
        
return dao.getContacts();
    }


    
public void updateItem(Object newItem, Object preItem, List changes)
    
{
        
// DataServiceTransaction dtx = DataServiceTransaction.getCurrentDataServiceTransaction();
        new ContactDAO().update((ContactVO)newItem, (ContactVO)preItem);
        
//((Contact)newItem).setFirstName("CCCC");
    }

}

 

Flex message service

Concepts

Producers -- Applications that send messages.
Consumers -- Applications that receive messages.
Message channel -- Connects producers and consumers to message destinations.
Message endpoint -- The code responsible for encoding data into messages, and decoding messages into a format that consumers can use.
Message adapter -- The code acting as a conduit between the Flex Message Service and other messaging system.

Supported messaging

Flex messaging
Provided by FDS 2, producers and customers are flex clients or Java objects which use Flex server-side API to send or receive messages.
Only support topic; and the only one supporting subtopic.
Use ActionScript adapter.
Java Message Service (JMS) messaging
Support all JMS messaging like IBM MQ, Sonic MQ, etc. Serve-side java objects use JMS API, and flex clients use client API.
Support topic and queue.
Use JMS adapter.
ColdFusion Component (CFC) messaging
Similar with JMS messaging, but with ColdFusion Event Gateway Adapter.
Custom messaging
Custom message adapter to support other messaging, should Inherit from flex.messaging.services.ServiceAdapter.

如何实现把message只发给特定的用户?

properties + selector

可以使用Cusumer类中的selector属性来接收特定的message, 可以传给selector一个字符串, 字符串中包含基于SQL92语法(http://www.linuxforum.net/books/postgresNEW/syntax.htm)的表达式.(Flex2 developer’s guide / p1194). 如:

<mx:consumer id="consumer" message="messageHandler(event)" selector="myName='CC'" destination="tony_message_chat"></mx:consumer>

selector是否有长度限制?? 好像没有.

Implements custom java adapter to set selectors in server-side??

Subtopic

“The subtopic feature lets you divide the messages that a Producer component sends to a destination into specific categories at the destination. You can configure a Consumer component that subscribes to the destination to receive only messages sent to a specific subtopic or set of subtopics. You use wildcard characters (*) to send or receive messages from more than one subtopic.”

“You cannot use subtopics with a JMS destination. However, you can use message headers and Consumer selector expressions to achieve similar functionality when using JMS.”

Cluster

当用Flex message service自己的message provider时, 它支持cluster. Flex 内配了对JGroup的支持来实现cluster. (Flex 2 Developer’s Guide / p1111). JGroups is an open-source program (http://www.jgroups.org/javagroupsnew/docs/index.html). FDS supports JGroups internally, and developers just need to do some configurations to use cluster with JGroups.

不过FDS的expression版只支持单CPU, 只有序列号才能支持cluster.

用户退出后怎么unsubscribe??

Flash Player also provides support for versions of RTMP that are tunneled through HTTP and HTTPS, 但Flex目前不支持. Flash Communication Server supports HTTP tunneling to get around this issue and extends services to the users behind proxy servers.

Flex Message Service也可以配置使用rtmp或amf polling (both secure or not) channel.

Flex messaging, JMS messaging (Topic/Queue)代码, 三者客户端部分相同, 服务端使用同一个VO, 如下:

Server-side (java codes)

MyMessageVO.java

/**
 * MyMessageVO.java
*/


package  tony.tonyMesChat;

import  java.io.Serializable;

/**
 * 
@author Tony
 
*/

public   class  MyMessageVO  implements  Serializable
{
    
private String name;
    
private String words;
    
    
public String getName()
    
{
        
return name;
    }


    
public void setName(String name)
    
{
        
this.name = name;
    }


    
public String getWords()
    
{
        
return words;
    }


    
public void setWords(String words)
    
{
        
this.words = words;
    }

    
    
public MyMessageVO(String name, String words)
    
{
        
this.name = name;
        
this.words = words;
    }

}

 

Client-side (AS, mxml codes)

MyMessageVO.as

//  MyMessageVO.as

package  tony.tonyMesChat
{
    
public class MyMessageVO
    
{
        
private var mname:String = "";
        
private var mwords:String = "";
        
        
public function get name():String
        
{
            
return mname;
        }

        
        
public function set name(name:String):void
        
{
            
this.mname = name;
        }

        
        
public function get words():String
        
{
            
return mwords;
        }

        
        
public function set words(words:String):void
        
{
            
this.mwords = words;
        }

    }

}

 

TonyMesChat.mxml

<? xml version="1.0" encoding="utf-8" ?>
< mx:Application  xmlns:mx ="http://www.adobe.com/2006/mxml"  layout ="vertical"
    creationComplete
="initChat()" >
    
< mx:Script >
        
<![CDATA[
            import mx.messaging.*;
            import mx.messaging.messages.*;
            import mx.messaging.events.*;
            import tony.tonyMesChat.MyMessageVO;
            
            private function initChat():void
            {
                consumer.subscribe();
                robotChat.talkOn();
            }
            
            private function sendMessage():void
            {
                var message:AsyncMessage = new AsyncMessage();
                message.headers = new Array();
                //message.headers["myName"] = "CC";
                message.body = new MyMessageVO();
                message.body.name = txtName.text;
                message.body.words = txtWords.text;
                producer.send(message);
                
                if (txtWords.text == "shutup")
                {
                    robotChat.shutUp();
                }
                else if (txtWords.text == "hibaby")
                {
                    robotChat.talkOn();
                }
                
                txtWords.text = "";
            }
            
            private function messageHandler(e:MessageEvent):void
            {
                var obj:Object = e.message.body;
                if (txtAllwords != null && obj.name != undefined)
                {
                    var temp:String = obj.name + ": " + obj.words + " ";
                    
                    txtAllwords.text += temp;
                    txtAllwords.validateNow();
                    txtAllwords.verticalScrollPosition=txtAllwords.maxVerticalScrollPosition;
                }
            }
            
            private function faultHandler(e:Event):void
            {
                trace("Something wrong");
                trace(e.toString());
            }
        
]]>
    
</ mx:Script >
    
< mx:Producer  id ="producer"  destination ="tony_message_chat" />
    
< mx:Consumer  id ="consumer"  destination ="tony_message_chat"  message ="messageHandler(event)" />
    
    
< mx:RemoteObject  id ="robotChat"  destination ="tony_message_chat_robot"  
           showBusyCursor
="false"  fault ="faultHandler(event)"   />
    
    
< mx:TextArea  id ="txtAllwords"  height ="247"  width ="282"  editable ="false" />
    
< mx:HBox  width ="{txtAllwords.width}" >
        
< mx:TextInput  id ="txtName"  width ="54" />
        
< mx:TextInput  id ="txtWords"  width ="{txtAllwords.width-txtName.width-10}"  enter ="sendMessage()" />
    
</ mx:HBox >
    
< mx:Button  label ="Send"  click ="sendMessage()" />
</ mx:Application >

 

不同的代码

Server-side

Flex messaging:

WebContent/WEB-INF/flex/messaging-config.xml 中相关配置

     < destination  id ="tony_message_chat" >
        
< properties >
            
<!--
            <network>
                <cluster ref="default-cluster"/>
            </network>
            
-->
            
< server >
                
< max-cache-size > 1000 </ max-cache-size >
                
< message-time-to-live > 0 </ message-time-to-live >
                
< durable > false </ durable >
            
</ server >
        
</ properties >

        
< channels >
            
< channel  ref ="my-rtmp" />
        
</ channels >
    
</ destination >

 

WebContent/WEB-INF/flex/remoting-config.xml 中相关配置

< destination  id ="tony_message_chat_robot" >
        
< properties >
            
< source > tony.tonyMesChat.RobotChat </ source >
            
< scope > application </ scope >
        
</ properties >
    
</ destination >

 

RobotChat.java

/**
 * RobotChat.java
*/


package  tony.tonyMesChat;

import  java.util.Random;

import  flex.messaging.MessageBroker;
import  flex.messaging.messages.AsyncMessage;
// import flex.messaging.util.UUIDUtils;

/**
 * 
@author p465890
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 
*/

public   class  RobotChat
{
    
private static Thread robot = null;
    
private static boolean talktalk = true;
    
private static String[] robotWords = {
            
"I'm robot CC.""Who are you?""Come on!""Oh shit!",
            
"I'm unhappy now...""????""!!!!"}
;
    
    
public void shutUp()
    
{
        talktalk 
= false;
    }

    
    
public void talkOn()
    
{
        
if (robot != null && talktalk)
        
{
            
return;
        }

        talktalk 
= true;
        robot 
= new Thread(new Runnable()
        
{
            
public void run()
            
{
                MessageBroker msgBroker 
= MessageBroker.getMessageBroker(null);
                
//String clientID = UUIDUtils.createUUID(false);;

                Random random 
= new Random();
                
while (talktalk)
                
{
                    AsyncMessage msg 
= new AsyncMessage();

                    msg.setDestination(
"tony_message_chat");
                    
// msg.setClientId(clientID);
                    
// msg.setMessageId(UUIDUtils.createUUID(false));
                    
// msg.setTimestamp(System.currentTimeMillis());
                    msg.setBody(new MyMessageVO("CCRobot", robotWords[random.nextInt(robotWords.length)]));
                    msgBroker.routeMessageToService(msg, 
null);
                    
try
                    
{
                        Thread.sleep((random.nextInt(
10000% 8 + 1* 1000);
                    }

                    
catch (InterruptedException e)
                    
{
                        e.printStackTrace();
                    }

                }

            }

        }
);
        robot.start();
    }

}

 

JMS messaging (topic):

WebContent/WEB-INF/flex/messaging-config.xml 中相关配置

< destination  id ="tony_message_chat" >
        
< properties >
            
< server >
                
< durable > false </ durable >
                
< durable-store-manager > flex.messaging.durability.FileStoreManager </ durable-store-manager >
            
</ server >

            
< jms >
                
< destination-type > Topic </ destination-type >
                
< message-type > javax.jms.ObjectMessage </ message-type >
                
< connection-factory > jms/flex/tony_message_chat_tcf </ connection-factory >
                
< destination-jndi-name > jms/topic/flex/tony_message_chat </ destination-jndi-name >
                
< destination-name > tony_message_chat </ destination-name >
                
< delivery-mode > NON_PERSISTENT </ delivery-mode >
                
< message-priority > DEFAULT_PRIORITY </ message-priority >
                
< acknowledge-mode > AUTO_ACKNOWLEDGE </ acknowledge-mode >
                
< transacted-sessions > false </ transacted-sessions >
            
</ jms >
        
</ properties >

        
< channels >
            
< channel  ref ="my-rtmp" />
        
</ channels >
        
        
< adapter  ref ="jms" />
    
</ destination >

 

WebContent/WEB-INF/flex/remoting-config.xml 中相关配置

< destination  id ="tony_message_chat_robot" >
        
< properties >
            
< source > tony.tonyMesChat.JMSRobotChat </ source >
            
< scope > application </ scope >
        
</ properties >
    
</ destination >

 

JMSRobotChat.java

/**
 * JMSRobotChat.java
*/


package  tony.tonyMesChat;

import  java.util.Properties;
import  java.util.Random;

import  javax.jms.Message;
import  javax.jms.ObjectMessage;
import  javax.jms.Session;
import  javax.jms.Topic;
import  javax.jms.TopicConnection;
import  javax.jms.TopicConnectionFactory;
import  javax.jms.TopicPublisher;
import  javax.jms.TopicSession;
import  javax.naming.Context;
import  javax.naming.InitialContext;

/**
 * 
@author Tony
 
*/

public   class  JMSRobotChat
{
    
private static Thread robot = null;
    
private static boolean talktalk = true;
    
private static String[] robotWords = {
            
"I'm robot CC.""Who are you?""Come on!""Oh shit!",
            
"I'm unhappy now...""????""!!!!"}
;
    
    
public void shutUp()
    
{
        talktalk 
= false;
    }

    
    
public void talkOn()
    
{
        
if (robot != null && talktalk)
        
{
            
return;
        }

        talktalk 
= true;
        robot 
= new Thread(new Runnable()
        
{
            
public void run()
            
{
                TopicSession pubSession;
                TopicPublisher publisher;
                TopicConnection connection;

                String _providerurl 
= "iiop://127.0.0.1:2809";
                
//adjust for your app server
                String _ctxtFactory = "com.ibm.websphere.naming.WsnInitialContextFactory";

                
try {
                    
// Obtain JNDI Context
                    Properties p = new Properties();
                    p.put(Context.PROVIDER_URL, _providerurl);
                    p.put(Context.INITIAL_CONTEXT_FACTORY, _ctxtFactory);
                    
//specific to your app server setup
                    
//p.put(Context.SECURITY_PRINCIPAL, "admin");
                    
//p.put(Context.SECURITY_CREDENTIALS , "admin");
                    Context context = new InitialContext(p);
                    TopicConnectionFactory factory 
= (TopicConnectionFactory) context.lookup("jms/flex/tony_message_chat_tcf");
                    
// Create a JMS connection
                    connection = factory.createTopicConnection();
                    
// Create publisher session
                    pubSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
                    Topic topic 
= (Topic) context.lookup("jms/topic/flex/tony_message_chat");
                    
// Create a publisher
                    publisher = pubSession.createPublisher(topic);

                    Random random 
= new Random();
                    
while (talktalk)
                    
{
                        ObjectMessage message 
= pubSession.createObjectMessage();
                        message.setStringProperty(
"myName""CC");
                        message.setObject(
new MyMessageVO("CCRobot", robotWords[random.nextInt(robotWords.length)]));
                        publisher.publish(message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, 
5 * 60 * 1000);
                        Thread.sleep((random.nextInt(
10000% 8 + 1* 1000);
                    }

                }
 
                
catch (Exception e) 
                
{
                    e.printStackTrace();
                }

            }

        }
);
        robot.start();
    }

}

 

JMS messaging (Queue):

WebContent/WEB-INF/flex/messaging-config.xml 中相关配置

< destination  id ="tony_message_chat" >

        
< properties >

            
< server >
                
< durable > false </ durable >
                
< durable-store-manager > flex.messaging.durability.FileStoreManager </ durable-store-manager >
            
</ server >

            
< jms >
                
< destination-type > Queue </ destination-type >
                
< message-type > javax.jms.ObjectMessage </ message-type >
                
< connection-factory > jms/flex/tony_message_chat_qcf </ connection-factory >
                
< destination-jndi-name > jms/queue/flex/tony_message_chat_queue </ destination-jndi-name >
                
< destination-name > tony_message_chat </ destination-name >
                
< delivery-mode > NON_PERSISTENT </ delivery-mode >
                
< message-priority > DEFAULT_PRIORITY </ message-priority >
                
< acknowledge-mode > AUTO_ACKNOWLEDGE </ acknowledge-mode >
                
< transacted-sessions > false </ transacted-sessions >
            
</ jms >
        
</ properties >

        
< channels >
            
< channel  ref ="my-rtmp" />
        
</ channels >

        
< adapter  ref ="jms" />
    
</ destination >

 

WebContent/WEB-INF/flex/remoting-config.xml 中相关配置

< destination  id ="tony_message_chat_robot" >
        
< properties >
            
< source > tony.tonyMesChat.JMSQueueRobotChat </ source >
            
< scope > application </ scope >
        
</ properties >
    
</ destination >

 

JMSQueueRobotChat.java

/**
 * JMSQueueRobotChat.java
*/


package  tony.tonyMesChat;

import  java.util.Properties;
import  java.util.Random;

import  javax.jms.Message;
import  javax.jms.ObjectMessage;
import  javax.jms.QueueSender;
import  javax.jms.QueueSession;
import  javax.jms.Session;
import  javax.jms.Queue;
import  javax.jms.QueueConnection;
import  javax.jms.QueueConnectionFactory;
import  javax.naming.Context;
import  javax.naming.InitialContext;

/**
 * 
@author Tony
 
*/

public   class  JMSQueueRobotChat
{
    
private static Thread robot = null;
    
private static boolean talktalk = true;
    
private static String[] robotWords = {
            
"I'm robot CC.""Who are you?""Come on!""Oh shit!",
            
"I'm unhappy now...""????""!!!!"}
;
    
    
public void shutUp()
    
{
        talktalk 
= false;
    }

    
    
public void talkOn()
    
{
        
if (robot != null && talktalk)
        
{
            
return;
        }

        talktalk 
= true;
        robot 
= new Thread(new Runnable()
        
{
            
public void run()
            
{
                QueueSession sendSession;
                QueueSender sender;
                QueueConnection connection;

                String _providerurl 
= "iiop://127.0.0.1:2809";
                String _ctxtFactory 
= "com.ibm.websphere.naming.WsnInitialContextFactory";

                
try {
                    Properties p 
= new Properties();
                    p.put(Context.PROVIDER_URL, _providerurl);
                    p.put(Context.INITIAL_CONTEXT_FACTORY, _ctxtFactory);
                    
//specific to your app server setup
                    
//p.put(Context.SECURITY_PRINCIPAL, "admin");
                    
//p.put(Context.SECURITY_CREDENTIALS , "admin");
                    Context context = new InitialContext(p);
                    QueueConnectionFactory factory 
= (QueueConnectionFactory) context.lookup("jms/flex/tony_message_chat_qcf");
                    connection 
= factory.createQueueConnection();
                    sendSession 
= connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
                    Queue queue 
= (Queue) context.lookup("jms/queue/flex/tony_message_chat_queue");
                    sender 
= sendSession.createSender(queue);

                    Random random 
= new Random();
                    
while (talktalk)
                    
{
                        ObjectMessage message 
= sendSession.createObjectMessage();
                        message.setObject(
new MyMessageVO("CCRobot", robotWords[random.nextInt(robotWords.length)]));
                        sender.send(message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, 
5 * 60 * 1000);
                        Thread.sleep((random.nextInt(
10000% 8 + 1* 1000);
                    }

                }

                
catch (Exception e) 
                
{
                    e.printStackTrace();
                }

            }

        }
);
        robot.start();
    }

}

 

JMS messaging + property of message header + selector代码

Server-side (java codes)

WebContent/WEB-INF/flex/remoting-config.xml 中相关配置

< destination  id ="tony_alert_demo_robot" >
        
< properties >
            
< source > tony.alertDemo.JMSAlertRobot </ source >
            
< scope > application </ scope >
        
</ properties >
    
</ destination >

 

AlertVO.java

/**
 * AlertVO.java
*/


package  tony.alertDemo;

import  java.io.Serializable;

/**
 * 
@author Tony
 
*/

public   class  AlertVO  implements  Serializable
{
    
public String words;
    
    
public AlertVO(String words)
    
{
        
this.words = words;
    }

}

 

JMSAlertRobot.java

/**
 * JMSAlertRobot.java
*/


package  tony.alertDemo;

import  java.util.ArrayList;
import  java.util.List;
import  java.util.Properties;
import  java.util.Random;

import  javax.jms.Message;
import  javax.jms.ObjectMessage;
import  javax.jms.Session;
import  javax.jms.Topic;
import  javax.jms.TopicConnection;
import  javax.jms.TopicConnectionFactory;
import  javax.jms.TopicPublisher;
import  javax.jms.TopicSession;
import  javax.naming.Context;
import  javax.naming.InitialContext;

import  tony.alertDemo.AlertVO;

/**
 * 
@author Tony
 
*/

public   class  JMSAlertRobot
{
    
private static Thread robot = null;
    
private static boolean talktalk = true;
    
private static int[] alertType = {1234};
    
private static String[] robotWords = {
            
"......""``````""~~~~~~""******",
            
"------""??????""!!!!!"}
;
    
private static List userIds = new ArrayList();
    
private static String[] clientIds = {"AAA""BBB"};
    
private static String[] appIds = {"CCC""DDD"};
     
    
public void getout(String userId)
    
{
        
if (!userIds.isEmpty())
        
{
            userIds.remove(userId);
        }

        
        
if (userIds.isEmpty())
        
{
            talktalk 
= false;
        }

    }

    
    
public void alertOn(String userId)
    
{
        userIds.add(userId);
        
        
if (robot != null && talktalk)
        
{
            
return;
        }

        talktalk 
= true;
        robot 
= new Thread(new Runnable()
        
{
            
public void run()
            
{
                TopicSession pubSession;
                TopicPublisher publisher;
                TopicConnection connection;

                String _providerurl 
= "iiop://127.0.0.1:2809";
                String _ctxtFactory 
= "com.ibm.websphere.naming.WsnInitialContextFactory";

                
try {
                    Properties p 
= new Properties();
                    p.put(Context.PROVIDER_URL, _providerurl);
                    p.put(Context.INITIAL_CONTEXT_FACTORY, _ctxtFactory);
                    Context context 
= new InitialContext(p);

                    TopicConnectionFactory factory 
= (TopicConnectionFactory) context.lookup("jms/flex/tony_alert_demo_tcf");
                    connection 
= factory.createTopicConnection();
                    pubSession 
= connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
                    Topic topic 
= (Topic) context.lookup("jms/topic/flex/tony_alert_demo");
                    publisher 
= pubSession.createPublisher(topic);

                    Random random 
= new Random();
                    
while (talktalk)
                    
{
                        ObjectMessage message 
= pubSession.createObjectMessage();
                        
int num = random.nextInt(100000% 6;
                        
                        
switch (num)
                        
{
                        
case 0:
                            message.setIntProperty(
"mt"1);
                            
break;
                        
case 1:
                            message.setIntProperty(
"mt"2);
                            message.setStringProperty(
"md", (String)(userIds.get(random.nextInt(10000% userIds.size())));
                            
break;
                        
case 2:
                        
case 3:
                            message.setIntProperty(
"mt"3);
                            message.setStringProperty(
"md", clientIds[random.nextInt(10000% clientIds.length]);
                            
break;
                        
case 4:
                        
case 5:
                            message.setIntProperty(
"mt"4);
                            message.setStringProperty(
"md", appIds[random.nextInt(10000% appIds.length]);
                            
break;
                        }


                        message.setObject(
new AlertVO(robotWords[random.nextInt(10000% robotWords.length]));
                        publisher.publish(message, Message.DEFAULT_DELIVERY_MODE, Message.DEFAULT_PRIORITY, 
5 * 60 * 1000);
                        Thread.sleep(
2000);
                    }

                }
 
                
catch (Exception e) 
                
{
                    e.printStackTrace();
                }

            }

        }
);
        robot.start();
    }

}

 

Client-side (mxml codes)

AlertDemo.mxml

<? xml version="1.0" encoding="utf-8" ?>
< mx:Application  xmlns:mx ="http://www.adobe.com/2006/mxml"  layout ="vertical"
    currentState
="logon"  creationComplete ="initApp()" >
    
    
< mx:Script >
        
<![CDATA[
            import mx.binding.utils.BindingUtils;
            import mx.messaging.events.MessageEvent;
            import mx.controls.Alert;
            import mx.messaging.Consumer;
            
            private var consumer:Consumer;
            private var userId:String;
            private var clientId:String;
            private var appId:String;
            
            [Bindable]
            private var clientIds:Array = ["AAA", "BBB"];
            
            [Bindable]
            private var appIds:Array = ["CCC", "DDD"];
                
            private function initApp():void
            {
                clientId = (String)(cbbClientIds.selectedItem);
                appId = (String)(cbbAppIds.selectedItem);
            }
            
            private function logon():void
            {
                userId = txtUserId.text;
                txtUserId.text = "";
                
                if (userId == "")
                {
                    Alert.show("Input user id first");
                    return;
                }
                
                consumer = new Consumer();
                consumer.destination = "tony_alert_demo";
                var strSelector:String = "mt=1 OR (mt=2 AND md='" + userId + 
                    "') OR (mt=3 AND md='" + clientId + 
                    "') OR (mt=4 AND (md='" + appId + "'";
                var i:int = 0;
                //for (i=0; i<600; i++)
                //{
                //    strSelector += (" OR md='app" + i + "'");
                //}
                strSelector += "))";
                //Alert.show(strSelector);
                consumer.selector = strSelector;
                consumer.addEventListener(MessageEvent.MESSAGE, messageHandler);
                consumer.subscribe();
                
                robotAlert.alertOn(userId);
                currentState = "alertView";
            }
            
            private function logout():void
            {
                consumer.unsubscribe();
                robotAlert.getout(userId);
                txtAlerts.text = "";
                cbbClientIds.selectedIndex = 0;
                cbbAppIds.selectedIndex = 0;
                currentState = "logon";
            }
            
            private function messageHandler(e:MessageEvent):void
            {
                var body:Object = e.message.body;
                var header:Object = e.message.headers;

                var alert:String = ";   Content: " + body.words + " ";
                switch (header.mt)
                {
                    case 1:
                        alert = "System message" + alert;
                        break;
                    case 2:
                        alert = "User message to " + header.md + alert;
                        break;
                    case 3:
                        alert = "Client message to " + header.md + alert;
                        break;
                    case 4:
                        alert = "Appliction message to " + header.md + alert;
                        break;
                }
                
                txtAlerts.text += alert;
                txtAlerts.validateNow();
                txtAlerts.verticalScrollPosition=txtAlerts.maxVerticalScrollPosition;
            }
        
]]>
    
</ mx:Script >
    
    
< mx:RemoteObject  id ="robotAlert"  destination ="tony_alert_demo_robot"  
           showBusyCursor
="false" />
    
    
< mx:states >
        
< mx:State  name ="logon" >
            
< mx:AddChild  position ="lastChild" >
                
< mx:VBox  height ="100%"  horizontalAlign ="center"  verticalAlign ="middle" >
                    
< mx:HBox  width ="100%" >
                        
< mx:Label  text ="User Id"  width ="54" />
                        
< mx:TextInput  id ="txtUserId" />
                    
</ mx:HBox >
                    
< mx:HBox  width ="100%" >
                        
< mx:Label  text ="Client Id"  width ="54" />
                        
< mx:ComboBox  id ="cbbClientIds"  dataProvider ="{clientIds}"  close ="clientId=(String)(ComboBox(event.target).selectedItem)"  width ="159" />
                    
</ mx:HBox >
                    
< mx:HBox  width ="100%" >
                        
< mx:Label  text ="App Id"  width ="54" />
                        
< mx:ComboBox  id ="cbbAppIds"  dataProvider ="{appIds}"  close ="appId=(String)(ComboBox(event.target).selectedItem)"  width ="158" />
                    
</ mx:HBox >
                    
< mx:Button  label ="Logon"  click ="logon()" />
                
</ mx:VBox >
            
</ mx:AddChild >     
        
</ mx:State >
        
< mx:State  name ="alertView" >
            
< mx:AddChild  position ="lastChild" >
                
< mx:VBox  height ="100%"  horizontalAlign ="center"  verticalAlign ="middle" >
                    
< mx:TextArea  id ="txtAlerts"  height ="263"  width ="352" />
                    
< mx:Button  label ="Logout"  click ="logout()" />
                
</ mx:VBox >
            
</ mx:AddChild >
        
</ mx:State >
    
</ mx:states >
</ mx:Application >

 

 

与J2EE某些框架整合

FDS可与spring, hibernate很好的结合在一起.

“In Flex Data Services we’ve added a factory facility so that when you want a component, you use this facroty facility and we’ve integrated that with Spring and we integrated that with EJB, so that will allow a client to directly call a remote object kind of Spring component without having the right glue or wrapper code.”

“For Hibernate Flex’s data management layer has a Hibernate adapter and this allows you to directly expose Hibernate object models directly to Flex clients. So without any coding, Flex clients can execute queries on Hibernate, get back lists or graphs of objects. Using Flex’s data binding support, we listen for changes that you make on the client to those objects, which queue up changes up to the server, commit them to do conflict detection. If there are any conflicts, report those conflicts back to the client, so the client can do either accept server or accept client and then we will also push those changes out to other clients that happen to be looking at that same data. So, this is a very easy way to quickly get your Hibernate models published to Flex clients. Now a lot of people don’t want to expose their Hibernate models directly to the client, so there is a way you can kind of extend that and filter it to restrict the model or add your own security constraints on top of the security constraints which Flex provides out of the box.”

使用文中代码

http://www.adobe.com/support/documentation/en/flex/2/install.html

安装Flex Data Service, 安装包中提供了JRun Server, 使用它可以直接把FDS自带的例子跑起来.

如果跑在其他Server或Container中, 比如Tomcat, Websphere等, 则需要有一点点的配置或相关代码的改动.

以RAD6作IDE, 跑在Websphere上为例, 要使用上文提到的代码:

导入FDS安装目录下的sample.war为新的工程, 把WebContent/WEB-INF/flex/services-config.xml中有关Websphere内容注释掉的地方把注释去掉即可. 如果本机的Websphere Server没有WorkManager的功能, 就不用管WorkManager那部分. 实际上FDS在使用RTMP时有专门利用Websphere的WorkManager作的性能优化, 不使用WorkManager程序跑起来是没问题的.

在相关位置添加上面那些codes, 具体位置见各codes中的注释. 对于需要JMS provider, 根据具体情况配置, 只要注间JNDI引用与代码中的一致. 代码在Websphere Test Environment 5.1 + Webshpere Embedded MQ下运行通过.JMS配置如截图:

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值