Использование функции Spring / DWR Reverse Ajax для отображения динамических изменений данных таблицы - PullRequest
0 голосов
/ 09 января 2012

В моем текущем проекте Spring / JSF мне нужно интегрировать метод для отправки обновлений на стороне сервера (предпочтительно, как они происходят) в веб-интерфейс для отображения клиентам.

Итак, чтобы начать работуЯ работаю над этим небольшим приложением, чтобы понять, как работает DWR.По сути, я разработал два варианта для реализации push на стороне сервера.

В первом подходе, когда нажата «кнопка поиска», обновления на стороне сервера отображаются правильно.Это вызывает метод findInstrumentSymbols () в InstrumentManager и возвращает выходные данные в виде объектов List через DWR.

Во втором подходе я попытался реализовать подход обратного ajax, предложенный на веб-сайте DWR. (http://directwebremoting.org/dwr-demo/reverseajax/peopleTable.html)

Как сделать эту функцию доступной в качестве функции по умолчанию каждый раз, когда страница загружается, без необходимости специально активировать ее с помощью кнопки?

В этой реализации мне не удалось вернуть вывод какСписок объектов через DWR, как в первом подходе и должен был быть отправлен в виде строкового массива. Я вижу, что новые результаты не заполняются должным образом, как в первом подходе, и это имеет тенденцию терять новые данные, когда «Отключить кнопку»Как это может быть достигнуто с помощью DWR?

Я перечислил фрагменты кода ниже. Буду очень признателен за любые указания по началу работы функции обратного Ajax.

Я использовалопрос, как описано в предыдущих выпусках DWR. Какой метод был бы наиболее идеальным для реализации функции обратного Ajax, если переход к DWR3 - это путь вперед?

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>

application-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 голосов
/ 14 января 2012

Разобрался с одной частью проблемы, но мне пока не повезло с передачей списка Java с использованием push на стороне сервера.

Я изучал демо-приложение peopleTable и аналогичные веб-приложения, чтобы найти решение для моей проблемы. Большинство из этих приложений передают массивы String или String.

* >>> * Это вызывает метод findInstrumentSymbols () в InstrumentManager и возвращает вывод в виде объекта через DWR.

Одна вещь, которую я заметил в DWR, заключалась в том, что при вызове Java-сценария DWR, связанного с операцией графического интерфейса, такой как Onclick (кнопка Egfind), данные типа объекта или коллекции могут передаваться плавно. Я считаю, что объект JSON возвращается этот экземпляр.

Методы findInstuments JSON выводят , как показано в программе, и могут быть легко интегрированы в веб-страницу.

>>> * Но когда вы попытались передать данные на веб-страницу с помощью операции push-запроса сервера, я не смог вернуть вывод, как раньше, через DWR, как при первом подходе. Его пришлось отправить в виде строкового массива (основываясь на информации, с которой я столкнулся *: | ).

Я основал пример кода Push сервера на разновидности демо peopleTable. Кажется, DWR предоставляет мало возможностей для отправки Collection или Objects в GUI, хотя он легко доступен, когда связан с пользовательской операцией, в которой передаются объекты JSON. (Кажется, он предлагает поддержку массивов String / 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 - что было легко (я надеюсь, что не пропустил что-то очевидное: |)

Существует ли лучший метод, который можно было бы разработать для передачи объектов при использовании push-запросов сервера с использованием этого метода? (прибегание к преобразованию строки, похоже, было предложено в большинстве веб-примеров.)

Можно ли обмениваться данными как объектами JSON, если передача на стороне сервера выполняется без триггера графического интерфейса для удовлетворения тех же требований? * Так какие дополнительные изменения конфигурации и обновления потребуются? *

Если передача данных на основе JSON возможна для Push на стороне сервера, не могли бы вы рассказать о каком-либо документе, сайте или образце кода, которые могут оказаться полезными. не нашел ничего конкретного по этой теме

Заранее спасибо

...