使用Spring / DWR反向Ajax功能显示对表数据的动态更改

时间:2012-01-09 09:38:44

标签: java javascript ajax spring dwr

在我目前的Spring / JSF项目中,我需要集成一个方法来将服务器端更新(最好是它们发生时)推送到基于Web的GUI,以显示给客户端。

因此,为了开始使用,我正在研究这个小应用程序,以了解DWR的工作原理。基本上,我已经开发了两种变体来实现服务器端推送。

在第一种方法中,当按下“查找按钮”时,正确显示后端更新。这将调用InstrumentManager中的findInstrumentSymbols()方法,并通过DWR将输出作为List Objects返回。

在第二种方法中,我试图实现DWR网站中建议的反向ajax方法。(http://directwebremoting.org/dwr-demo/reverseajax/peopleTable.html

每次加载页面时如何使此功能可用作默认功能,而无需通过按钮专门激活它?

在这个实现中,我无法像第一种方法那样通过DWR将输出作为List Objects返回,并且必须作为字符串数组发送。我看到新结果没有像第一种方法那样正确填充,并且当按下“禁用按钮”时,它往往会丢失新数据。如何使用DWR正确实现反向服务器推送功能?

我已经列出了下面的代码片段。我真的很感激任何关于使反向Ajax功能运行的指针。

我已经使用了早期DWR版本中描述的轮询。如果切换到DWR3是前进的方法,那么实现反向Ajax功能最理想的方法是什么?

Web.xml中

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" 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">

    <display-name>AdminConsole</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/application-context.xml
        /WEB-INF/application-security-context.xml</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    <context-param>
        <description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    <context-param>
        <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
        <param-value>resources.application</param-value>
    </context-param>
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>
    <listener>
        <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <servlet>
        <display-name>DWR Servlet</display-name>
        <servlet-name>dwr-invoker</servlet-name>
        <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>activeReverseAjaxEnabled</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>org.directwebremoting.extend.ServerLoadMonitor</param-name>
            <param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value>
        </init-param>
        <init-param>
            <param-name>timeToNextPoll</param-name>
            <param-value>5000</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>
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
    <error-page>
        <exception-type>javax.faces.application.ViewExpiredException</exception-type>
        <location>/login.xhtml</location>
    </error-page>
    .......
</web-app>

的dwr.xml

<?xml version="1.0" encoding="UTF-8"?>
<DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://directwebremoting.org/schema/dwr30.dtd">

<dwr>
  <allow>
    <convert converter="bean" match="com.stocktrade.admin.entity.*"/>
    <create creator="spring" javascript="InstrumentManager">
      <param name="beanName" value="instrumentManager"/>
    </create>   
  </allow>
</dwr>

应用context.xml中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring- context.xsd                       
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/jee
       http://www.springframework.org/schema/jee/spring-jee.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.directwebremoting.org/schema/spring-dwr
       http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd">

       .....

    <!-- bean for Instrument class -->
    <bean id="instrumentManager"  class="com.stocktrade.admin.instrument.InstrumentManagerImpl">
        <property name="instrumentDAO" ref="instrumentDAO"></property>
        <property name="userManager" ref="userManager"></property>
        <property name="accountManager" ref="accountManager"></property>
        <property name="portfolioManager" ref="portfolioManager"></property>
    </bean>

      ......
</beans>

乐器类

package com.xxxxx.admin.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "instrument")
public class Instrument implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="symbolName")
    private String SymbolName;

    @Column(name="name")
    private String Name;

    @Column(name="quantity")
    private long Quantity;

    @Column(name="price")
    private BigDecimal Price;

    @Column(name="Limit")
    private int Limit;

    public String getSymbolName() {
        return SymbolName;
    }
    public void setSymbolName(String symbolName) {
        SymbolName = symbolName;
    }
    public String getName() {
        return Name;
    }
    public void setName(String name) {
        Name = name;
    }
    public long getQuantity() {
        return Quantity;
    }
    public void setQuantity(long quantity) {
        Quantity = quantity;
    }
    public BigDecimal getPrice() {
        return Price;
    }
    public void setPrice(BigDecimal price) {
        Price = price;
    }
    public int getLimit() {
        return Limit;
    }
    public void setLimit(int Limit) {
        Limit = Limit;
    }
}

InstrumentManager类

package com.xxxxx.admin.instrument; 

import java.math.BigDecimal;
import java.util.List;
import com. xxxxx.admin.entity.Instrument;
import com. xxxxx.admin.entity.Portfolio;
import com. xxxxx.admin.entity.Account;
import com. xxxxx.admin.entity.Users;

public interface InstrumentManager {    
        ………
        /*used to implement the find() functionality*/
    public List<Instrument> findInstrumentSymbols();
        /*used to implement the reverse ajax functionality*/
    public void SendInstrumentSymbols();
    public void addAttributeToScriptSession();
    public void removeAttributeToScriptSession();
    public void run();
}

InstrumentManagerImpl类

package com.xxxxx.admin.instrument;

import java.math.BigDecimal;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;

import com.xxxxx.admin.account.AccountManager;
import com.xxxxx.admin.dao.InstrumentDAO;
import com.xxxxx.admin.entity.Instrument;
import com.xxxxx.admin.entity.Account;
import com.xxxxx.admin.entity.Portfolio;
import com.xxxxx.admin.entity.Users;
import com.xxxxx.admin.portfolio.PortfolioManager;
import com.xxxxx.admin.user.UserManager;

import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.util.Logger;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ScriptSessionFilter;
import org.directwebremoting.Browser;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.impl.DaemonThreadFactory;
import org.directwebremoting.ui.dwr.Util;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class InstrumentManagerImpl implements InstrumentManager,Runnable {

    private final static String SCRIPT_SESSION_ATTR = "SCRIPT_SESSION_ATTR";
    public static String getScriptSessionAttr() {
        return SCRIPT_SESSION_ATTR;
    }

    private UserManager userManager;
    public UserManager getUserManager() {
        return userManager;
    }
    public void setUserManager(UserManager userManager) {
        this.userManager = userManager;
    }

    private InstrumentDAO instrumentDAO;
    public InstrumentDAO getInstrumentDAO() {
        return instrumentDAO;
    }
    public void setInstrumentDAO(InstrumentDAO instrumentDAO) {
        this.instrumentDAO = instrumentDAO;
    }

    public InstrumentManagerImpl() {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory());
        executor.scheduleAtFixedRate(this, 1, 10, TimeUnit.SECONDS);
    }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run()
    {
        SendInstrumentSymbols();
    }
        /*used to implement the find() functionality*/
    @Override
    public List<Instrument> findInstrumentSymbols() {
        return instrumentDAO.findInstrumentSymbols();
    }
    /*used to implement the reverse ajax functionality*/
    public void SendInstrumentSymbols() {
        // Get the current page.
        String page = ServerContextFactory.get().getContextPath() + "/faces/gehan.xhtml";
        // Create a new AttributeScriptSessionFilter which will look for an attribute on the ScriptSession
        ScriptSessionFilter attributeFilter = new AttributeScriptSessionFilter(SCRIPT_SESSION_ATTR);
        // Update the page, filters ScriptSessions using attributeFilter.  If the SCRIPT_SESSION_ATTR
        // has not been set on the ScriptSession the page in question will not receive updates.
        Browser.withPageFiltered(page, attributeFilter, new Runnable()
        {
            @Override
            public void run()
            {
                List<Instrument> InstrumentList = new ArrayList<Instrument>();
                InstrumentList = findInstrumentSymbols() ;
            //Creates a multi-dimensional array, containing a row and the rows column data.
            //Added the First Element of the List returned - values are converted to String
                String[][] data = {
                    {InstrumentList.get(0).getSymbolName().toString(), InstrumentList.get(0).getName().toString(), String.valueOf(InstrumentList.get(0).getQuantity()), InstrumentList.get(0).getPrice().toString(), String.valueOf(InstrumentList.get(0).getMarketMakingLimit())}};

   // Call DWR's util which adds rows into a table.peopleTable is the id of the tbody and 
      data contains the row/column data.  
                Util.addRows("MMTable", data);
            }
        });
    }

    /**
     * Called from the client to add an attribute on the ScriptSession.  This
     * attribute will be used so that only pages (ScriptSessions) that have 
     * set this attribute will be updated.
     */
    public void addAttributeToScriptSession() {
        ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        scriptSession.setAttribute(SCRIPT_SESSION_ATTR, true);
    }

    /**
     * Called from the client to remove an attribute from the ScriptSession.  
     * When called from a client that client will no longer receive updates 
         * (unless addAttributeToScriptSession)
     * is called again.
     */
    public void removeAttributeToScriptSession() {
        ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        scriptSession.removeAttribute(SCRIPT_SESSION_ATTR);
    }

   /**
     * This is the ScriptSessionFilter that will be used to filter out all ScriptSessions
     * unless they contain the SCRIPT_SESSION_ATTR attribute. 
     */
    protected class AttributeScriptSessionFilter implements ScriptSessionFilter
    {
        private final String attributeName;

        public AttributeScriptSessionFilter(String attributeName)
        {
            this.attributeName = attributeName;
        }

        @Override
        public boolean match(ScriptSession session)
        {
            Object check = session.getAttribute(attributeName);
            return (check != null && check.equals(Boolean.TRUE));
        }
    }

}

updateMMStatus.xhtml

<!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" 
      xmlns:ui="http://java.sun.com/jsf/facelets" 
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:f="http://java.sun.com/jsf/core">
      <h:head>
      <title>Reverse Ajax Table Update</title>
      <meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
      <script type='text/javascript' src='/AdminConsole/dwr/engine.js'></script>
      <script type='text/javascript' src='/AdminConsole/dwr/util.js'></script>
      <script type='text/javascript' src='/AdminConsole/dwr/interface/InstrumentManager.js'></script>

      <script type="text/javascript">
            window.onload=function()
            {
                            // Initiate reverse ajax polling
                dwr.engine.setActiveReverseAjax(true);
                            // Called when a call and all retry attempts fail 
                dwr.engine.setErrorHandler(errorHandler);
                            // Optional function to call when the reverse ajax status changes (e.g. online to offline) 
                dwr.engine.setPollStatusHandler(updatePollStatus);
                            // Optional - We are online right now!  Until DWR determines we are not! 
                updatePollStatus(true);
                            //Optional - When the page is unloaded, remove this ScriptSession. 
                dwr.engine.setNotifyServerOnPageUnload(true);
                            // Initialize the tabs for this display         
                Tabs.init('tabList', 'tabContents'); 
                            // Make a call to the server to begin updating the table!   
                InstrumentManager.SendInstrumentSymbols();
                            //Make a remote call to the server to add an attribute onto the ScriptSession 
                            //which will be used in determining what pages receive updates! 
                addAttributeToScriptSession(); 
            }
                function errorHandler(message, ex) {
                    dwr.util.setValue("error", "Cannot connect to server. Initializing retry logic.", {escapeHtml:false});
                    setTimeout(function() { dwr.util.setValue("error", ""); }, 5000)
                }

                function updatePollStatus(pollStatus) {
                    dwr.util.setValue("pollStatus", pollStatus ? "Online" : "Offline", {escapeHtml:false});
                }

                // Make a remote call to add an attribute on the ScriptSession.
                // Only clients that have this attribute set will receive updates.    
                function addAttributeToScriptSession() {
                    InstrumentManager.addAttributeToScriptSession();
                }

                // Make a remote call to remove an attribute from the ScriptSession.
                // Clients that call this will no longer receive updates (unless addAttributeToScriptSession is called again).        
                function removeAttributeToScriptSession() {
                    InstrumentManager.removeAttributeToScriptSession();
                }
      </script>
    /*Java script to updated instrument table related changes when the "find" Button is pressed */
    function find() {
      <script type="text/javascript">
      function find() {
        InstrumentManager.findInstrumentSymbols();
      }
    </script>
    </h:head>
    <h:body onload="dwr.engine.setActiveReverseAjax(true);">

    <input type="button" value="Enable page updates" onclick="addAttributeToScriptSession();"/>
    <input type="button" value="Disable page updates" onclick="removeAttributeToScriptSession();"/>

        <h:messages styleClass="error" />
        <div class="data_header">Current Symbols in the System</div>
        <h:dataTable rowClasses="odd,even" id="MMTable"
            value="#{updateMMStausBean.instrumentsList}" var="insList"
            rendered="#{updateMMStausBean.instrumentsList != null}"
            headerClass="tableHeader" styleClass="table">
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Symbol" />
                </f:facet>
                <div align="left">
                    <h:outputText value="#{insList.symbolName}" />
                </div>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Name" />
                </f:facet>
                <div align="left">
                    <h:outputText value="#{insList.name}" />
                </div>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Quantity" />
                </f:facet>
                <div align="center">
                    <h:outputText value="#{insList.quantity}" />
                </div>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Price($)" />
                </f:facet>
                <div align="right">
                    <h:outputText value="#{insList.price}" />
                </div>
            </h:column>
            <h:column>
                <f:facet name="header">
                    <h:outputText value="Limit" />
                </f:facet>
                <div align="right">
                    <h:outputText value="#{insList.mMLimit}" />
                </div>
            </h:column>
        </h:dataTable>

        <div id="container">    
    <h:panelGrid columns="2" cellpadding="6">
    <h:form id="authenticate1">
    <h:commandButton action="#{updateMMStausBean.ActivateAllMMOrdrerOn()}" value="Activate all Orders">
    </h:commandButton>
    </h:form>

    <h:form id="authenticate2">
    <h:commandButton action="#{updateMMStausBean.DeactivateAllMMOrdrerOn()}" value="Deactivate all Orders">
    </h:commandButton>
    </h:form>

        /*  Java script to updated instrument table related changes when the "find" Button is pressed */
    <h:form id="authenticate3">
    <h:commandButton value="Find" onclick="find()">
    </h:commandButton>
    </h:form>  
    </h:panelGrid>
</div>

      </h:body>

</html>

1 个答案:

答案 0 :(得分:0)

排除了问题的一部分,但我还没有运气,因为现在使用服务器端推送传递了一个java List。

我一直在研究基于网络的peopleTable demo application和类似的应用程序,为我的问题提出解决方案。这些应用程序中的大多数都传递String或String数组。

* &gt;&gt;&gt; *这会调用InstrumentManager中的findInstrumentSymbols()方法,并通过DWR将输出作为Object返回。

我在DWR中注意到的一件事是,当调用DWR java脚本链接到GUI操作(如Onclick(Egfind按钮))时,可以顺利传递Object或集合类型数据。我相信返回JSON对象这个例子。

程序中显示的findInstuments methods JSON output,可以轻松集成到网页中。

&gt;&gt;&gt; * 但是当您尝试使用服务器推送操作将数据传递到网页时,我无法像以前那样通过DWR返回输出,就像第一种方法一样。它有作为字符串数组发送(根据我遇到的信息* :| )。

我将服务器Push示例代码基于peopleTable演示的变体。似乎DWR提供了很少的工具来将集合或对象发送到GUI,尽管在链接到传递JSON对象的用户操作时它很容易获得。 (它似乎支持String / String数组和基于字符串的集合,或者我收集的是:|

下面列出了与此描述相关的相关代码示例。(完整代码参考原始帖子)

public void SendInstrumentSymbols() {
        // Get the current page.
        String page = ServerContextFactory.get().getContextPath() + "/faces/Instruments.xhtml";
        // Create a new AttributeScriptSessionFilter which will look for an attribute on the ScriptSession
        ScriptSessionFilter attributeFilter = new AttributeScriptSessionFilter(SCRIPT_SESSION_ATTR);
        // Update the page, filters ScriptSessions using attributeFilter.  If the SCRIPT_SESSION_ATTR
        // has not been set on the ScriptSession the page in question will not receive updates.
        Collection sessions = ServerContextFactory.get().getScriptSessionsByPage(page);
        Browser.withPageFiltered(page, attributeFilter, new Runnable()
        {
            <b>@Override
            public void run()
            {
                // Creates a new Person bean.
                List<Instrument> InstrumentList = new ArrayList<Instrument>();
                InstrumentList = findInstrumentSymbols() ;

        /*This seems to be the tricky part - Sending the List<Instrument> to the server end.*/

                // Creates a multi-dimensional array, containing a row and the rows column data.
                String[][] data = {
                    {InstrumentList.get(0).getSymbolName().toString(), InstrumentList.get(0).getName().toString(), String.valueOf(InstrumentList.get(0).getQuantity()), InstrumentList.get(0).getPrice().toString(), String.valueOf(InstrumentList.get(0).getMarketMakingLimit()),String.valueOf(InstrumentList.get(0).isBuyOrderMarketMakingOn()),String.valueOf(InstrumentList.get(0).isSellOrderMarketMakingOn())}};
                // Call DWR's util which adds rows into a table.  instrumentTable is the id of the tbody and 
                // data contains the row/column data.  
                Util.addRows("instrumentTable", data);
            }
        }</b>);
    }

当你试图改变这个功能以支持其他数据类型时,List我找不到一个Clean方法,就像第一个传递Json对象的实例一样 - 这很容易。(我希望我没有错过显而易见的事情:|)

在使用此方法使用服务器推送时,是否有更好的方法可以设计传输对象?(求助于字符串转换似乎是大多数网络示例中建议的方法。)< / p>

在没有GUI触发器的情况下执行服务器端推送以满足相同要求时,可以将数据作为JSON对象进行交换吗? * 是否需要进行其他配置更改和更新? *

如果基于JSON的数据传输对于服务器端推送是可能的,那么请您对某些可能有任何帮助的文档,站点或示例代码提出一些看法。任何这样的领导都会非常感谢我没有找到关于这个主题的具体内容

先谢谢你