flex4 + spring + blazeds , 使用anonation(注解)机制,利用push技术的实现例子和过程。

实现目标:java做后台service,每隔300毫秒,生成一个uuid,以 testJob做为订阅关键词,发布给所有订阅此关键词的flex客户端。

 

配置过程和源码:

 

1.修改blazeds自动生成的WEB-INF/flex/services-config.xml文件。由于原来没有polling-amf的定义,所以需要加入。代码如下:

 

[xhtml:nogutter] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <services-config>  
  3.     <services>  
  4.         <service-include file-path="remoting-config.xml" />  
  5.         <service-include file-path="proxy-config.xml" />  
  6.         <service-include file-path="messaging-config.xml" />          
  7.     </services>  
  8.     <security>  
  9.         <login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"/>  
  10.         <!-- Uncomment the correct app server  
  11.         <login-command class="flex.messaging.security.TomcatLoginCommand" server="JBoss">  
  12.         <login-command class="flex.messaging.security.JRunLoginCommand" server="JRun"/>          
  13.         <login-command class="flex.messaging.security.WeblogicLoginCommand" server="Weblogic"/>  
  14.         <login-command class="flex.messaging.security.WebSphereLoginCommand" server="WebSphere"/>  
  15.         -->  
  16.         <!--   
  17.         <security-constraint id="basic-read-access">  
  18.             <auth-method>Basic</auth-method>  
  19.             <roles>  
  20.                 <role>guests</role>  
  21.                 <role>accountants</role>  
  22.                 <role>employees</role>  
  23.                 <role>managers</role>  
  24.             </roles>  
  25.         </security-constraint>  
  26.          -->  
  27.     </security>  
  28.     <channels>  
  29.         <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">  
  30.             <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>  
  31.         </channel-definition>  
  32.         <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">  
  33.             <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/>  
  34.             <properties>  
  35.                 <add-no-cache-headers>false</add-no-cache-headers>  
  36.             </properties>  
  37.         </channel-definition>  
  38.          <channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">  
  39.             <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>  
  40.             <properties>  
  41.                 <polling-enabled>true</polling-enabled>  
  42.                 <polling-interval-seconds>4</polling-interval-seconds>  
  43.             </properties>  
  44.         </channel-definition>  
  45.           
  46.         <channel-definition id="my-longpolling-amf" class="mx.messaging.channels.AMFChannel">  
  47.             <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amflongpolling" class="flex.messaging.endpoints.AMFEndpoint"/>  
  48.             <properties>  
  49.                 <polling-enabled>true</polling-enabled>  
  50.                 <polling-interval-seconds>5</polling-interval-seconds>  
  51.                 <wait-interval-millis>60000</wait-interval-millis>  
  52.                 <client-wait-interval-millis>1</client-wait-interval-millis>  
  53.                 <max-waiting-poll-requests>200</max-waiting-poll-requests>  
  54.                 <user-agent-settings>  
  55.                     <!-- MSIE 5, 6, 7 default max number of permanent HTTP connections is 2. -->  
  56.                     <user-agent match-on="MSIE" max-streaming-connections-per-session="1"/>  
  57.                     <!-- MSIE 8 max number is 6. -->  
  58.                     <user-agent match-on="MSIE 8" max-streaming-connections-per-session="5"/>  
  59.                     <!-- Firefox 1, 2 max number is 2. -->   
  60.                     <user-agent match-on="Firefox" max-streaming-connections-per-session="1"/>  
  61.                     <!-- Firefox 3 max number is 6. -->  
  62.                     <user-agent match-on="Firefox/3" max-streaming-connections-per-session="5"/>  
  63.                     <!-- Safari 3, 4 max number is 4. -->    
  64.                     <user-agent match-on="Safari" max-streaming-connections-per-session="3"/>  
  65.                     <!-- Chrome 0, 1, 2 max number is 6. -->  
  66.                     <user-agent match-on="Chrome" max-streaming-connections-per-session="5"/>  
  67.                     <!-- Opera 7, 9 max number is 4.-->  
  68.                     <user-agent match-on="Opera" max-streaming-connections-per-session="3"/>  
  69.                     <!-- Opera 8 max number is 8. -->    
  70.                     <user-agent match-on="Opera 8" max-streaming-connections-per-session="7"/>  
  71.                     <!-- Opera 10 max number is 8. -->  
  72.                     <user-agent match-on="Opera 10" max-streaming-connections-per-session="7"/>  
  73.                 </user-agent-settings>  
  74.             </properties>  
  75.         </channel-definition>          
  76.           
  77.         <channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">  
  78.             <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>  
  79.         </channel-definition>  
  80.         <!--  
  81.         <channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel">  
  82.             <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/http" class="flex.messaging.endpoints.HTTPEndpoint"/>  
  83.         </channel-definition>  
  84.         <channel-definition id="my-secure-http" class="mx.messaging.channels.SecureHTTPChannel">  
  85.             <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/httpsecure" class="flex.messaging.endpoints.SecureHTTPEndpoint"/>  
  86.             <properties>  
  87.                 <add-no-cache-headers>false</add-no-cache-headers>  
  88.             </properties>  
  89.         </channel-definition>  
  90.         -->  
  91.     </channels>  
  92.     <logging>  
  93.         <target class="flex.messaging.log.ConsoleTarget" level="Error">  
  94.             <properties>  
  95.                 <prefix>[BlazeDS] </prefix>  
  96.                 <includeDate>false</includeDate>  
  97.                 <includeTime>false</includeTime>  
  98.                 <includeLevel>false</includeLevel>  
  99.                 <includeCategory>false</includeCategory>  
  100.             </properties>  
  101.             <filters>  
  102.                 <pattern>Endpoint.*</pattern>  
  103.                 <pattern>Service.*</pattern>  
  104.                 <pattern>Configuration</pattern>  
  105.             </filters>  
  106.         </target>  
  107.     </logging>  
  108.     <system>  
  109.         <redeploy>  
  110.             <enabled>false</enabled>  
  111.             <!--   
  112.             <watch-interval>20</watch-interval>  
  113.             <watch-file>{context.root}/WEB-INF/flex/services-config.xml</watch-file>  
  114.             <watch-file>{context.root}/WEB-INF/flex/proxy-config.xml</watch-file>  
  115.             <watch-file>{context.root}/WEB-INF/flex/remoting-config.xml</watch-file>  
  116.             <watch-file>{context.root}/WEB-INF/flex/messaging-config.xml</watch-file>  
  117.             <watch-file>{context.root}/WEB-INF/flex/data-management-config.xml</watch-file>  
  118.             <touch-file>{context.root}/WEB-INF/web.xml</touch-file>  
  119.              -->  
  120.         </redeploy>  
  121.     </system>  
  122. </services-config>  

 

 

2.在spring的配置文件中定义 defaultMessageTemplate class="org.springframework.flex.messaging.MessageTemplate"

 

[xhtml:nogutter] view plaincopy
  1. <bean id="defaultMessageTemplate" class="org.springframework.flex.messaging.MessageTemplate" />  

 

 

之所以要定义这个bean,是为了在做service的时候。可以用注解@Autowired来引入这个MessageTemplate的实例。

3.在web.xml中定义spring和flex的集成。这个配置是使用spring和flex集成时候的配置,和push技术无关。

 

[xhtml:nogutter] view plaincopy
  1. <!-- 定义 spring flex 集成的配置,使用单独的配置文件。  
  2.        不使用在一个applicatonContext.xml中import springFlex.xml的方法   
  3.        spring flex 一定要用DispatcherServlet 方法。不能用ContextLoaderListener -->  
  4.    <servlet>  
  5.        <servlet-name>MessagebrokerServlet</servlet-name>  
  6.        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  7.        <init-param>  
  8.            <param-name>contextConfigLocation</param-name>  
  9.            <param-value>/WEB-INF/classes/META-INF/springFlex.xml</param-value>  
  10.        </init-param>  
  11.        <load-on-startup>1</load-on-startup>  
  12.    </servlet>  
  13.    <!-- Map /spring/* requests to the DispatcherServlet -->  
  14.    <servlet-mapping>  
  15.        <servlet-name>MessagebrokerServlet</servlet-name>  
  16.        <url-pattern>/messagebroker/*</url-pattern>  
  17.    </servlet-mapping>  
  18.    <!-- 定义 spring flex 集成的配置  结束-->  
  19.      
  20.    <!--  访问RDS 定义开始-->  
  21.    <servlet>  
  22.        <servlet-name>RDSDispatchServlet</servlet-name>  
  23.        <servlet-class>flex.rds.server.servlet.FrontEndServlet</servlet-class>  
  24.        <init-param>  
  25.            <param-name>useAppserverSecurity</param-name>  
  26.            <param-value>false</param-value>  
  27.        </init-param>  
  28.        <init-param>  
  29.            <param-name>messageBrokerId</param-name>  
  30.            <param-value>_messageBroker</param-value>  
  31.        </init-param>  
  32.        <load-on-startup>10</load-on-startup>  
  33.    </servlet>  
  34.    <servlet-mapping id="RDS_DISPATCH_MAPPING">  
  35.        <servlet-name>RDSDispatchServlet</servlet-name>  
  36.        <url-pattern>/CFIDE/main/ide.cfm</url-pattern>  
  37.    </servlet-mapping>  
  38.    <!--  访问RDS 定义结束 -->  

 

 

4.由于web.xml配置中引入了WEB-INF/classes/META-INF/springFlex.xml这个文件,所以在springFlex.xml文件中进行详细配置

5.springFlex.xml文件如下,主要是定义flex:message-destination ,既是监听的数据的关键词

 

[xhtml:nogutter] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans  
  3.     xmlns="http://www.springframework.org/schema/beans"  
  4.     xmlns:flex="http://www.springframework.org/schema/flex"  
  5.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  7.        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  8.        http://www.springframework.org/schema/flex  
  9.        http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">  
  10.      
  11.       
  12.      <flex:message-broker>  
  13.         <flex:message-service  
  14.             default-channels="my-polling-amf" />  
  15.     </flex:message-broker>  
  16.       
  17.     <flex:message-destination id="simple-feed" />  
  18.     <flex:message-destination id="testJob-feed"/>  
  19.       
  20.       
  21. </beans>  
  22.    

 

 

6.编写后台推送的java service bean:

 

[java:nogutter] view plaincopy
  1. /*********************************************************************** 
  2.   * 
  3.   *   TestJob.java 
  4.   * 
  5.   *   ****所有, 
  6.   *   受到法律的保护,任何公司或个人,未经授权不得擅自拷贝。 
  7.   *   @copyright       Copyright:   2000-2011   
  8.   *   @creator          徐泽宇 <br/> 
  9.   *   @create-time   2011-6-27   下午11:51:16 
  10.   *   @revision         $Id:     * 
  11.   ***********************************************************************/   
  12. package com.alcor.test.service.quartz;  
  13. import java.util.Date;  
  14. import java.util.UUID;  
  15. import org.slf4j.Logger;  
  16. import org.slf4j.LoggerFactory;  
  17. import org.springframework.beans.factory.annotation.Autowired;  
  18. import org.springframework.flex.messaging.MessageTemplate;  
  19. import org.springframework.flex.remoting.RemotingDestination;  
  20. import org.springframework.flex.remoting.RemotingInclude;  
  21. import org.springframework.scheduling.annotation.Scheduled;  
  22. import org.springframework.stereotype.Service;  
  23. import flex.messaging.messages.AsyncMessage;  
  24. @Service  
  25. @RemotingDestination  
  26. public class TestJob {  
  27.     /** 
  28.      * Logger for this class 
  29.      */  
  30.     private static final Logger logger = LoggerFactory.getLogger(TestJob.class);  
  31.     @Autowired  
  32.     MessageTemplate template;  
  33.       
  34.     private static FeedThread thread;  
  35.       
  36.     @RemotingInclude  
  37.     public void work3 (){  
  38.         if (thread == null) {  
  39.             thread = new FeedThread(template);  
  40.             thread.start();  
  41.         }  
  42.     }  
  43.       
  44.     public static class FeedThread extends Thread {  
  45.         public boolean running = false;  
  46.         private final MessageTemplate template;  
  47.         public FeedThread(MessageTemplate template) {  
  48.             this.template = template;  
  49.             run();  
  50.         }  
  51.         @Override  
  52.         public void run() {  
  53.             this.running = true;  
  54.             while (this.running) {  
  55.             logger.debug("testJob-feed send");  
  56.             this.template.send("testJob-feed", UUID.randomUUID().toString());  
  57.             // this.template.send("testJob-feed", UUID.randomUUID().toString());  
  58.                 try {  
  59.                     Thread.sleep(300);  
  60.                 } catch (InterruptedException e) {  
  61.                     e.printStackTrace(System.out);  
  62.                 }  
  63.             }  
  64.         }  
  65.     }  
  66. }  

 

 

注意:这里使用了@Service,@@RemotingDestination 注解和 @Autowired    MessageTemplate template; 这样就避免了在spring中利用配置文件进行定义bean。

如果要使用spring 配置文件来定义。可以参考下面代码(来自blazeds的官方例子中的spring配置文件)

 

[xhtml:nogutter] view plaincopy
  1. <!-- MessageTemplate makes it easy to publish messages -->  
  2.    <bean id="defaultMessageTemplate" class="org.springframework.flex.messaging.MessageTemplate" />  
  3.    <!-- Pojo used to start and stop the data feed that pushes data in the 'simple-feed' destination -->  
  4.    <bean id="simpleFeedStarter" class="org.springframework.flex.samples.simplefeed.SimpleFeed">  
  5.        <constructor-arg ref="defaultMessageTemplate" />  
  6.        <flex:remoting-destination />  
  7.    </bean>  

 

 

 

 

 

具体的业务实现是在 内联线程类FeedThread 里面的run()中实现。

 

这要做的目标是为了让这个线程启动,是一定要通过flex的一个客户端连上来,才能启动。而不能随着spring context启动而启动。否则会报告messageBroke没有设置在flexContext之类的错误。

 

那么:如果我们想后台在没有任何一个客户端连上来的情况下,也要启动这个发送服务应该如何做?

典型场景: 后台web应用启动,并且定义了一个定时器。每隔 5s 进行一个逻辑处理,然后把处理结果发送给前台 flex(无论是否有felx客户端连上)

 

这就需要用下面代码:

 

[java:nogutter] view plaincopy
  1. public String work2(){  
  2.         if (logger.isDebugEnabled()) {  
  3.             logger.debug("work2() - start"); //$NON-NLS-1$  
  4.         }  
  5.         String m_rtn  ="Spring 的TestJob任务work2被调用!" ;  
  6.           
  7.         String returnString =new Date().toLocaleString()+ m_rtn;  
  8.         //------------------------------------------  
  9.          MessageBroker msgBroker = MessageBroker.getMessageBroker("_messageBroker");    
  10.          AsyncMessage am = new AsyncMessage();    
  11.          am.setDestination("testJob-feed");    
  12.          //am.setHeader("DSSubtopic", "tick");    
  13.          am.setClientId(UUID.randomUUID().toString());    
  14.          String msgID =UUID.randomUUID().toString();    
  15.          //System.out.println("MESSAGE ID:" + msgID);    
  16.          am.setMessageId(msgID);    
  17.          am.setTimestamp(System.currentTimeMillis());    
  18.          am.setBody(returnString);    
  19.          if (msgBroker != null)  
  20.          {  
  21.              msgBroker.routeMessageToService(am, null);  
  22.          }  
  23.          //------------------------------------------  
  24.           
  25.         if (logger.isDebugEnabled()) {  
  26.             logger.debug("work2() - end"); //$NON-NLS-1$  
  27.         }  
  28.         return returnString;  
  29.     }  

 

 

这个work2 方法可以通过 quartz或者spring 的@Scheduled注解来进行定时器调用。

主要的关键代码是 

 MessageBroker msgBroker = MessageBroker.getMessageBroker("_messageBroker");  

 

这个就是获得一个 _messageBroker的实例。"_messageBroker"字符串是由 <flex:message-broker > 标签中的id来指定。

如果没有id 指定,则缺省使用 “"_messageBroker"”这个字符串

 

 

7.前台的订阅flex程序:

 

[xhtml:nogutter] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"   
  3.                xmlns:s="library://ns.adobe.com/flex/spark"   
  4.                xmlns:mx="library://ns.adobe.com/flex/mx">  
  5.       
  6.     <fx:Script>  
  7.         <!--[CDATA[  
  8.             import mx.controls.Alert;  
  9.             import mx.messaging.messages.IMessage;  
  10.               
  11.             private function messageHandler(message:IMessage):void  
  12.             {  
  13.                 pushedValue.text = ""+ message.body;  
  14.             }  
  15.               
  16.             private function messageHandler1(message:IMessage):void  
  17.             {  
  18.                 pushedValue1.text = ""+ message.body;  
  19.             }  
  20.               
  21.         ]]-->  
  22.     </fx:Script>  
  23.       
  24.     <fx:Declarations>  
  25.         <s:ChannelSet id="cs">  
  26.             <s:StreamingAMFChannel uri="/Aerie/messagebroker/streamingamf"/>  
  27.             <s:AMFChannel uri="/Aerie/messagebroker/amflongpolling"/>  
  28.             <s:AMFChannel uri="/Aerie/messagebroker/amfpolling"/>  
  29.         </s:ChannelSet>  
  30.           
  31.         <mx:Consumer id="consumer" destination="simple-feed" channelSet="{cs}"   
  32.                      message="messageHandler(event.message)"/>  
  33.         <mx:Consumer id="consumer1" destination="testJob-feed" channelSet="{cs}"   
  34.                      message="messageHandler1(event.message)"/>  
  35.     </fx:Declarations>  
  36.     <s:HGroup>  
  37.         <s:Group>  
  38.             <s:TextInput id="pushedValue" width="250" verticalCenter="0" horizontalCenter="0"/>  
  39.             <s:Button label="spimle-feed 订阅" click="consumer.subscribe()" enabled="{!consumer.subscribed}" verticalCenter="30" horizontalCenter="-50"/>  
  40.             <s:Button label="Unsubscribe" click="consumer.unsubscribe()" enabled="{consumer.subscribed}" verticalCenter="30" horizontalCenter="50"/>  
  41.         </s:Group>  
  42.         <s:Group>  
  43.             <s:TextInput id="pushedValue1" width="250" verticalCenter="0" horizontalCenter="0"/>  
  44.             <s:Button label="testJob 订阅" click="consumer1.subscribe()" enabled="{!consumer1.subscribed}" verticalCenter="30" horizontalCenter="-50"/>  
  45.             <s:Button label="Unsubscribe" click="consumer1.unsubscribe()" enabled="{consumer1.subscribed}" verticalCenter="30" horizontalCenter="50"/>  
  46.         </s:Group>  
  47.           
  48.     </s:HGroup>  
  49.       
  50.       
  51.       
  52. </s:Application>  

 

 

其中关键代码是:

 

[xhtml:nogutter] view plaincopy
  1. <fx:Declarations>  
  2.         <s:ChannelSet id="cs">  
  3.             <s:StreamingAMFChannel uri="/Aerie/messagebroker/streamingamf"/>  
  4.             <s:AMFChannel uri="/Aerie/messagebroker/amflongpolling"/>  
  5.             <s:AMFChannel uri="/Aerie/messagebroker/amfpolling"/>  
  6.         </s:ChannelSet>  
  7.           
  8.         <mx:Consumer id="consumer" destination="simple-feed" channelSet="{cs}"   
  9.                      message="messageHandler(event.message)"/>  
  10.         <mx:Consumer id="consumer1" destination="testJob-feed" channelSet="{cs}"   
  11.                      message="messageHandler1(event.message)"/>  
  12.     </fx:Declarations>  

 

 

注意:

<s:StreamingAMFChannel uri="/Aerie/messagebroker/streamingamf"/>

<s:AMFChannel uri="/Aerie/messagebroker/amflongpolling"/>

<s:AMFChannel uri="/Aerie/messagebroker/amfpolling"/>

这三个通道的设置中用到的

 Aerie是代表context名字。

messagebroker 是代码 web.xml中的配置的MessagebrokerServlet中的url-pattern

amfpolling 等是指 在services-config.xml文件中设置的 channel-definition 下的endpoint 名字

posted @ 2012-02-18 13:31  张良  阅读(1715)  评论(0编辑  收藏  举报