2012-01-31 61 views
3

我为一个冗长的问题表示歉意,但我真的需要你的帮助。作为我们项目的一部分,我目前正在研究一个搜索引擎,它可以即时更新结果列表:用户键入前4个字符,然后键入结果列表更改。搜索值在文本框中键入,而结果显示在下面的RichData组件rich:Table中。如果搜索值被删除,结果列表为空。然而,在我尝试了几次之后,我得到了组件本身抛出的ConcurrentModificationException。我正在搜索的初始列表来自属性文件(因为我不希望搜索在用户输入内容时触及数据库)。几个月来,我一直在嘲笑它。我错过了什么?让我告诉你我做了什么:使用Richfaces的ConcurrentModificationException扩展数据表

这是应该触发搜索逻辑的输入文本(当数值少于4个字符时,我确保表格不会更新,或者如果用户按下键,似箭,Shift和CTRL - 这个功能是 “returnunicode(事件)”):

<h:inputText id="firmname" value="#{ExtendedTableBean.searchValue}"> 
     <a4j:support reRender="resultsTable" onsubmit=" 
      if ((this.value.length<4 && this.value.length>0) || !returnunicode(event)) { 
       return false; 
      }" actionListener="#{ExtendedTableBean.searchForResults}" event="onkeyup" /> 
    </h:inputText> 

动作监听器是什么都要更新列表。这里是extendedDataTable,正下方的inputText:

<rich:extendedDataTable tableState="#{ExtendedTableBean.tableState}" var="item" 
          id="resultsTable" value="#{ExtendedTableBean.dataModel}"> 

      ... <%-- I'm listing columns here --%> 

    </rich:extendedDataTable> 

现在,如果它的确定,我想向您展示后端代码。我只留下了对我的问题很重要的逻辑。

/* 
    * To change this template, choose Tools | Templates 
    * and open the template in the editor. 
    */ 

package com.beans; 

import java.io.FileInputStream; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.ConcurrentModificationException; 
import java.util.List; 
import java.util.Properties; 
import java.util.concurrent.CopyOnWriteArrayList; 
import javax.faces.context.FacesContext; 
import javax.faces.event.ActionEvent; 
import org.richfaces.model.DataProvider; 
import org.richfaces.model.ExtendedTableDataModel; 

public class ExtendedTableBean {  
    private String sortMode="single"; 
    private ExtendedTableDataModel<ResultObject> dataModel; 
    //ResultObject is a simple pojo and getResultsPerValue is a method that 
    //read the data from the properties file, assigns it to this pojo, and 
    //adds a pojo to the list 

    private Object tableState; 
    private List<ResultObject> results = new CopyOnWriteArrayList<ResultObject>(); 
    private List<ResultObject> selectedResults = 
              new CopyOnWriteArrayList<ResultObject>(); 

    private String searchValue; 

    /** 
     * This is the action listener that the user triggers, by typing the search value 
     */ 
    public void searchForResults(ActionEvent e) { 
     synchronized(results) { 
      results.clear(); 
     }   

     //I don't think it's necessary to clear results list all the time, but here 
     //I also make sure that we start searching if the value is at least 4 
     //characters long 
     if (this.searchValue.length() > 3) { 
      results.clear(); 
      updateTableList(); 
     } else { 
      results.clear(); 
     } 

     dataModel = null; // to force the dataModel to be updated. 
    } 

    public List<ResultObject> getResultsPerValue(String searchValue) { 
     List<ResultObject> resultsList = new CopyOnWriteArrayList<ResultObject>(); 

     //Logic for reading data from the properties file, populating ResultObject 
     //and adding it to the list 

     return resultsList; 
    } 

    /** 
     * This method updates a firm list, based on a search value 
     */ 
    public void updateTableList() { 
     try {    
      List<ResultObject> searchedResults = getResultsPerValue(searchValue); 

      //Once the results have been retrieved from the properties, empty 
      //current firm list and replace it with what was found. 

      synchronized(firms) { 
       firms.clear(); 
       firms.addAll(searchedFirms); 
      } 
     } catch(Throwable xcpt) { 
      //Exception handling 
     } 
    } 

    /** 
     * This is a recursive method, that's used to constantly keep updating the 
     * table list. 
     */ 
    public synchronized ExtendedTableDataModel<ResultObject> getDataModel() { 
     try { 
      if (dataModel == null) { 
       dataModel = new ExtendedTableDataModel<ResultObject>(
          new DataProvider<ResultObject>() { 
           public ResultObject getItemByKey(Object key) { 
            try { 
            for(ResultObject c : results) { 
             if (key.equals(getKey(c))){ 
              return c; 
             } 
            } 
            } catch (Exception ex) { 
            //Exception handling 
            } 
            return null; 
           } 

           public List<ResultObject> getItemsByRange(
                int firstRow, int endRow) { 
            return Collections.unmodifiableList(results.subList(firstRow, endRow)); 
           } 

           public Object getKey(ResultObject item) { 
            return item.getResultName(); 
           } 

           public int getRowCount() { 
            return results.size(); 
           } 
          }); 
      } 
     } catch (Exception ex) { 
      //Exception handling  
     } 

     return dataModel; 
    } 

    //Getters and setters 

} 

就像我说的,它工作正常,但是当快速的用户类型或删除快(这是很难赶上正好当它发生时),ConcurrentModificationException的异常。这里是什么样子完全相同:

WARNING: executePhase(RENDER_RESPONSE 6,[email protected]) threw exception 
java.util.ConcurrentModificationException 
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) 
    at java.util.AbstractList$Itr.next(AbstractList.java:343) 
    at org.richfaces.model.ExtendedTableDataModel.walk(ExtendedTableDataModel.java:108) 
    at org.ajax4jsf.component.UIDataAdaptorBase.walk(UIDataAdaptorBase.java:1156) 
    at org.richfaces.renderkit.AbstractExtendedRowsRenderer.encodeRows(AbstractExtendedRowsRenderer.java:159) 
    at org.richfaces.renderkit.AbstractExtendedRowsRenderer.encodeRows(AbstractExtendedRowsRenderer.java:142) 
    at org.richfaces.renderkit.AbstractExtendedRowsRenderer.encodeChildren(AbstractExtendedRowsRenderer.java:191) 
    at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:812) 
    at org.ajax4jsf.renderkit.RendererBase.renderChild(RendererBase.java:277) 
    at org.ajax4jsf.renderkit.AjaxChildrenRenderer.encodeAjaxComponent(AjaxChildrenRenderer.java:166) 
    at org.ajax4jsf.renderkit.AjaxChildrenRenderer.encodeAjaxChildren(AjaxChildrenRenderer.java:83) 
    at org.ajax4jsf.renderkit.AjaxChildrenRenderer.encodeAjaxComponent(AjaxChildrenRenderer.java:157) 
    ... 

的Java代码同步从来就不是我最强的一面,我不知道会是什么引起的错误,最重要的是,我怎么能摆脱它。我知道,Richfaces 4.0已经对rich:extendedDataTable组件做了很多改变,我听说这是一个问题,现在已经解决了。但是,我没有时间将整个应用程序升级到Richfaces 4.0(这将在第2阶段完成),这只是整个项目的一小部分。如果没有办法解决上述问题,那么可能有一种解决方法?或者,也许还有其他方法可以使用普通的JSF来实现类似的搜索(只要它足够快就可以实现)。对于这个问题,我会很感激任何帮助或建议。我希望代码足够容易理解,但如果没有,请告诉我,我会进一步解释。预先感谢您,我非常感谢您的帮助。

回答

2

问题是并发的ajax调用服务器。使用a4j:support中的属性“eventsQueue”。通常情况下,您应该始终在任何ajax组件中使用“eventsQueue”,同一页面中的所有“eventsQueue”引用相同的队列,除非您有充分的理由不这样做。

此外,你可能会想看看另一个ajax属性:“ajaxSingle”。

+0

谢谢,这正是我需要的。我之前使用过ajaxSingle,只是没有考虑过。然而,EventsQueue对我来说是新的。原来那很简单。再次感谢,这真的有帮助。 – 2012-02-01 08:23:50