DWR3 学习笔记

本文阐述了在视频处理场景中,如何运用DWR技术进行前端发起任务、JNI录制、c++SO转换,并通过spring3监听器实现消息的实时推送。重点介绍了消息处理流程、DWR逆向推送机制及与Spring的集成方式。此外,讨论了在实施过程中遇到的异常处理、上下文获取等问题及其解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习需要有强大的内心作为后盾,耐着性子,一步一个脚印的向前摸索。


选择DWR的原因:

前端发起录制视频任务传送到JNI录制接口,通过c++ so进行转换,转换后调用 message handle 通知客户端。

提出问题:
  
转换时间不确定,转换结果不确定,不能及时反馈给客户,客户如果建立个长连接,会阻塞客户的其他业务的处理。

整体设计:
底层服务利用spring3 监听器进行so消息转换结果的监听,如果so有消息返回,则利用dwr reverse ajax 把消息推送到指定用户。

详细设计:
step1 利用spring监听器,如果so消息发布,则publish到spring容器中。
代码如下:
@Service("messageHandler" )
public  class  MessageHandlerImp  implements  IMessageHandler,
            ApplicationContextAware {
        final  static  Logger  log  = Logger.getLogger(MessageHandlerImp. class );
        @Autowired
        private  IMessageService  messageService ;
        private  final  ArrayList<IMessage>  messages  =  new  ArrayList<IMessage>();
        @Autowired
        private  MpsMessageController  mpsMessageController ;
        private  ApplicationContext  context  ;
      
        public  void  handler(IMessage msg) {
              // mpsMessageController.convertResult();
              //messageService.save(msg);
              context .publishEvent( new  InfoMessage(msg));
      }

        @Override
        public  void  setApplicationContext(ApplicationContext context)
                    throws  BeansException {
            
              this . context  = context;
      }

}

step2:DWR ajax resvers是通过DWR ScripteSessionLisener进行scriptSession的创建。推送原理是 ,每一个用户请求会建立一个
scriptSession的数据通道,Dwr reverse 技术通过scriptsesession 发送到web端,再利用 scriptSession 与HttpSession相结合区分每一个用户的请求。
这样解决了避免多个用户都收到转换结果,从而达到,who 发起,who 接受的单点数据通信。
/**
 *
 * 扩展ScriptSessionManager.
 *
 *  @author  maxc
 *  @version  2012  - 2 -  15
 */
public  class  InitScriptManager  extends  DefaultScriptSessionManager {
        final  static  Logger  log  = Logger.getLogger(InitScriptManager. class );

        public  InitScriptManager() {
            addScriptSessionListener(  new  ScriptSessionListener() {
                    public  void  sessionCreated(ScriptSessionEvent event) {
                        ScriptSession scriptSession = event.getSession();  // 获取新创建的SS
                        HttpSession httpSession = WebContextFactory.get().getSession();  // 获取构造SS的用户的HttpSession
                        String userId = (String) httpSession.getAttribute( "userId"  );
                          if  (userId ==  null ) {
                              scriptSession.invalidate();
                              httpSession.invalidate();
                                return ;
                        }
                          log .info( "创建  ==============="  + scriptSession.getId());
                        scriptSession.setAttribute(  "userId" , userId); // 此处将userId和scriptSession绑定
                  }

                    public  void  sessionDestroyed(ScriptSessionEvent event) {
                          log .info( "销毁  ==============="  + event.getSession().getId());
                  }
            });
              /**
             *  @author  xiaochao.ma
             *      <p> 不用spring3注入是因为 一旦交给spring3容器处理,就会重新生成一个 mannager,反转业务就不能使用唯一的监听器监听scriptSession的创建  <p>
             *          
             *    注入DWRReservers 使得反转业务可以应用scriptManager 这样就可以获得当前应用的scriptSession
             */
            DWRReservers.  manager  =  this  ;

      }

}

step3:配置dwr
<?xml version="1.0" encoding= "UTF-8"?>
< web-app  version = "2.5"  xmlns = "http://java.sun.com/xml/ns/javaee"
        xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  >

        <!-- 默认的spring配置文件是在WEB-INF下的applicationContext.xml Spring 容器启动监听器 -->
        < listener >
              < listener-class >  org.springframework.web.context.ContextLoaderListener  </ listener-class >

        </ listener >

        < context-param >
              < param-name >  springconfig </ param-name  >
              < param-value >  /WEB-INF/applicationContext.xml,/WEB-INF/spring3-servlet.xml  </ param-value >

        </ context-param >
        < filter >
              < filter-name >  Set Character Encoding </ filter-name  >
              < filter-class >  org.springframework.web.filter.CharacterEncodingFilter  </ filter-class >
              < init-param >
                    < param-name >  encoding </ param-name  >
                    < param-value >  UTF-8 </ param-value  >
              </ init-param >
              < init-param >
                    < param-name >  forceEncoding </ param-name  >
                    < param-value >  true </ param-value  > <!-- 强制进行转码 -->
              </ init-param >
        </ filter >

        < filter-mapping >
              < filter-name >  Set Character Encoding </ filter-name  >
              < url-pattern >  /* </  url-pattern >
        </ filter-mapping >

        <!-- 默认所对应的配置文件是WEB-INF下的{ servlet-name}-servlet.xml,这里便是:spring3-servlet.xml -->
        < servlet >
              < servlet-name >  spring3 </ servlet-name  >
              < servlet-class >  org.springframework.web.servlet.DispatcherServlet </ servlet-class  >
              < init-param >
                    < param-name >  contextConfigLocation </ param-name  >
                    < param-value >
                        /WEB-INF/applicationContext.xml,/WEB-INF/spring3-servlet.xml
                    </ param-value >
              </ init-param >
              < load-on-startup >  1 </  load-on-startup >

        </ servlet >
        <!--dwr servlet -->

        < servlet-mapping >
              < servlet-name >  spring3 </ servlet-name  >
              <!-- 这里可以用 / 但不能用 /* ,拦截了所有请求会导致静态资源无法访问,所以要在spring3-servlet.xml中配置mvc:resources -->
              < url-pattern >  / </  url-pattern >
        </ servlet-mapping >
<!--  <servlet>
            <servlet-name>initScript</servlet-name>
            <servlet-class>com.gnetis.mpagent.controller.InitScriptSession</servlet-class>

            <load-on-startup>2</load-on-startup>
       </servlet> -->
        < servlet >
              < servlet-name >  dwr-invoker  </ servlet-name >
              < servlet-class >  org.directwebremoting.servlet.DwrServlet </ servlet-class  >
              <!-- <servlet-class>org.directwebremoting.spring.DwrSpringServlet</servlet-class> -->

              < init-param >
                    < param-name >  debug </ param-name  >
                    < param-value >  false </ param-value  >
              </ init-param >
              < init-param >
                    < param-name >  crossDomainSessionSecurity </ param-name  >
                    < param-value >  false </ param-value  >
              </ init-param >
              < init-param >
                    < param-name >  pollAndCometEnabled </ param-name  >
                    < param-value >  true </ param-value  >
              </ init-param >
              < init-param >
                    < param-name >  allowScriptTagRemoting </ param-name  >
                    < param-value >  true </ param-value  >
              </ init-param >
              < init-param >   
<!--此处非常关键:是建立当系统启动时,建立dwr监听-->
             < param-name >  org.directwebremoting.extend.ScriptSessionManager </ param-name  >  
             < param-value >  com.gnetis.mpagent.controller.InitScriptManager </ param-value  >
         </ init-param >
              < load-on-startup >  1 </  load-on-startup >
        </ servlet >

        < servlet-mapping >
              < servlet-name >  dwr-invoker  </ servlet-name >
              < url-pattern >  /dwr/*  </ url-pattern >
        </ servlet-mapping >
        < welcome-file-list >
              < welcome-file >  index.jsp </ welcome-file  >
        </ welcome-file-list >
</ web-app >

step3:Spring3监听到消息后,交由DWR ajax reverse技术进行推送。
代码如下:
package com.gnetis.mpagent.service.imp;

import java.util.Collection;
import java.util.concurrent.ScheduledThreadPoolExecutor;

import javax.servlet.ServletContext;

import org.apache.log4j.Logger;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.guice.ScriptSessionScoped;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.ServletContextAware;

import com.gnetis.mpagent.controller.InitScriptManager;
import com.gnetis.mpagent.service.MpsConvertTaskResultService;
import com.gnetis.tang.agent.message.IMessage;
import com.gnetis.tang.agent.message.InfoMessage;

public class DWRReservers implements ApplicationListener, ServletContextAware {

       @Autowired
       private MpsConvertTaskResultService mpsConvertTaskResultService ;
       final static Logger log = org.apache.log4j.Logger
                  . getLogger(DWRReservers.class);
       static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
                  1);
       public static InitScriptManager manager;
       private ServletContext servletContext;

       @RemoteMethod
       @ScriptSessionScoped
       public void reverseMessage(final IMessage message) {

             log.info("DWRReservers  reverseMessage :: active[" + active
                        + "] message[" + message + "]" );
             if (message == null) {
                   active = true ;

            } else {
                   log.info(" reverseMessage  :: message====>" + message);
                  Collection<ScriptSession> ss = manager.getAllScriptSessions();

                   for (ScriptSession scriptsession : ss) {
                        Object userId = scriptsession.getAttribute("userId" );
                         log.info("ScriptSessionId [" + scriptsession.getId()
                                    + "]userId[" + scriptsession.getAttribute("userId" )
                                    + "]");

                         if (userId != null
                                    && (userId.toString()).equals(message.getSessionId())) {
                              scriptsession.addScript( new ScriptBuffer()
                                          .appendScript( "receiveMessages").appendScript("(" )
                                          .appendData(message).appendScript( ");"));// 找到符合要求的,推送事件
                        }
                  }

                   active = false ;

            }

      }

       public DWRReservers() {
      }

       public synchronized void isReceive(final int confId, final String sessionId) {
             log.info(" isRecive :: jsp invork function paremeter are [" + confId
                        + "]");
             mpsConvertTaskResultService.convert(confId, sessionId);

      }

       /**
       * is receive
       */
       protected transient boolean active = true;

       @Override
       public void setServletContext(ServletContext servletContext) {

             this.servletContext = servletContext;
      }

       @Override
       public void onApplicationEvent(ApplicationEvent event) {
             // TODO Auto-generated method stub
             if (event instanceof InfoMessage) {
                  IMessage message = (IMessage) event.getSource();
                   log.info("onApplicationEvent :: message [" + message.getMsgID()
                              + " " + message.getSessionId() + " ]" );

                  reverseMessage(message);
            }
      }
}
step4:jsp发起的请求通道。
<%@ page language= "java" contentType ="text/html; charset=utf-8"
       pageEncoding="utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 
<%@page import="org.directwebremoting.ScriptSession" %>
<%@page import="org.directwebremoting.Container" %>
<%@page import="org.directwebremoting.ServerContextFactory" %>
<%@page import="org.directwebremoting.extend.ScriptSessionManager" %>
<%@page import="org.directwebremoting.event.ScriptSessionListener" %>
<%
      String path = request.getContextPath();
      String basePath = request.getScheme() + "://"
                  + request.getServerName() + ":" + request.getServerPort()
                  + path + "/";
%>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>

<title> Insert title here</title >
<style type="text/css" >
table {
       width: 600px ;
       margin: 0px auto ;
       font: Georgia 11px ;
       color: #333333 ;
       text-align: center ;
       border-collapse: collapse ;
}

tr {
       border: 1px solid black ;
       margin: 0px ;
       cellspacing: "1" cellpadding :"0"
}

td {
       border: 1px solid blue ;
       margin: 0px ;
       cellspacing: "1" cellpadding :"0"
}
</style>
<script type='text/javascript' src='/mp/dwr/engine.js'></ script>
<script type='text/javascript' src='/mp/dwr/interface/dwrService.js' ></script>
<script type='text/javascript' src='/mp/dwr/util.js'></ script>

<script type="text/javascript" >
dwr.engine._errorHandler=function(message ,ex){
         dwr.engine._debug( "Error: " + ex.name + ", " + ex.message, true);
          if (message == null || message == "") alert( "A server error has occurred.");
          // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
          else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message);
          else ;//alert(message);
}
       function dwrconvert(confId) {
             //alert(confId);
      dwrService.isReceive(confId, '<%=session.getAttribute("userId" ).toString()%>');
      
      
      }
       function objectEval(text) {
             // eval() breaks when we use it to get an object using the { a:42, b:'x' }
             // syntax because it thinks that { and } surround a block and not an object
             // So we wrap it in an array and extract the first element to get around
             // this.
             // This code is only needed for interpreting the parameter input fields,
             // so you can ignore this for normal use.
             // The regex = [start of line][whitespace]{[stuff]}[whitespace][end of line]
            text = text.replace(/\n/g, ' ');
            text = text.replace(/\r/g, ' ');
             if (text.match(/^\s*\{.*\}\s*$/)) {
                  text = '[' + text + '][0]' ;
            }
             return eval(text);
      }

       function receiveMessages(data) {
      
             if (data != null && typeof data == 'object') {
                   var jsMessage = objectEval(dwr.util.toDescriptiveString(data, 2));

                   var message = "" ;
                   var confId = jsMessage["confId" ];
                   var msgID = jsMessage["msgID" ];
                  message = "confId : [" + confId + "] , msgID : [" + msgID + "]";
                   var errorId = jsMessage["errorId" ];
                   var progressValue = jsMessage["progressValue" ];
                   var url = jsMessage["url" ];
                   if (errorId) {
                        message += ",errorId : [" + errorId + "]" ;
                  }
                   if (progressValue) {
                        message += ",progressValue : [" + progressValue + "]";
                  }

                   if (url) {

                         /*    var href=document.createElement("a");
                              href.attributes("href",url); */

                        message += ",url : <a href='"+url+"'> " + url + "</a>";
                  }
                  document.getElementById( "d0").innerHTML = message;
                   //dwr.util.setValue('d0', message);
            } else {

                  dwr.util.setValue( 'd0', dwr.util.toDescriptiveString(data, 1));
            }
      }

</script>
</head>
<body
       onload="dwr.engine.setActiveReverseAjax(true);dwr.engine.setNotifyServerOnPageUnload(true);" >
       <div id= "d0"></div >
       <div>
             <table>
                   <tr>
                         <td> ID</ td>
                         <td> 会议ID</td >
                         <td> 实例ID</td >
                         <td> 业务类型</td >
                         <td> 开始时间</td >
                         <td> 结束时间</td >
                         <td> 发布时间</td >
                         <td> site</td >
                         <td> modelId</td >
                         <td> convert</td >
                   </tr>

                   <c:forEach var="maps" items="${tasks } " >


                               <c:forEach var="o" items=" ${maps.mpsConvertTasks}" varStatus="sta" >
                                     <tr>
                                     <td> ${ o.id}</td >
                                     <td> ${ o.confId}</td >
                                     <td> ${ o.groupID}</td >
                                     <td> ${ o.serviceType}</td >
                                     <td> ${ o.startTime}</td >
                                     <td> ${ o.endTime}</td >
                                     <td> ${ o.addTime}</td >
                                     <td> ${ o.site}</td >
                                     <td> ${ o.modelId}</td >
                                     <c:if test=" ${sta.count==1 }">
                                           <td rowspan=" ${fn:length(maps.mpsConvertTasks) }">
                                     <a href="#" onclick="dwrconvert('${ o.id}')">converr </a>
                                           </td>
                                     </c:if>
                              
                               </tr>
                               </c:forEach>
                  
                        




                   </c:forEach>


             </table>
       </div>
</body>
</html>



总结: 如果用spring3的 DwrServelt配置反转mananger  报:
No bean named '__dwrConfiguration' is defined 
原因不明,猜测是由于dwr版本问题造成的。DWR反转与spring3相结合的comet方式没有很好得解决方案,dwr在spring容器下不能获得context,本项目采用分离处理。不用 spring实现的dwrservlet。详细见web.xml配置。
DWR.xml 代码:
<?xml version="1.0" encoding= "UTF-8"?>
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
"http://www.getahead.ltd.uk/dwr/dwr10.dtd" >
<dwr>
       <allow>
             <create creator="spring" javascript= "dwrService">
                   <param name="beanName" value="dwrService" />
             </create>
             <convert converter="bean"
                   match="com.gnetis.tang.agent.message.MessageProgress" />
             <convert converter="bean"
                   match="com.gnetis.tang.agent.message.MessageSuccess" />
             <convert converter="bean"
                   match="com.gnetis.tang.agent.message.MessageFailure" />
       </allow>
</dwr>
异常2:自己写servlet监听scriptSession的创建汇报  WebContextFactory. get()为NULL 异常,DWR context 必须在servlet容器中,自己写的servlet 没有发起请求时候,init是不能够获取到httpSession的。解决方案就是在DWR 自己的context中配置参数,实现defaultScriptSessionManager。详细见 web.xml。
异常3:dwr消息错误监听,当没有sessionScript建立时候,后台调用invalidSession方法会被dwr._debug接受并错误的弹出alert.
解决方案,重写dwr处理消息的机制,此项目中忽略了错误消息,若有需要可以写到后台日志中。
代码:
dwr.engine._errorHandler= function(message ,ex){
         dwr.engine._debug( "Error: " + ex.name + ", " + ex.message, true );
           if  (message ==  null  || message ==  "" ) alert(  "A server error has occurred." );
           // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
           else   if  (message.indexOf(  "0x80040111" ) !=  - 1) dwr.engine._debug(message);
           else  ; //alert(message); 忽略第一没建立数据通道的错误提示。当正确建立数据通道后,平台不会报此错误。
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值