2012-01-31 2 views
3

Прошу прощения за длинный вопрос, но мне очень нужна ваша помощь. В рамках нашего проекта я в настоящее время работаю над поисковой системой, которая обновляет список результатов «на лету»: пользователь вводит первые 4 символа и вверх, и по мере того, как он печатает, список результатов изменяется. Значение поиска вводится в текстовое поле, а результаты отображаются в богатом компоненте Richfaces: extendedDataTable ниже. Если значение поиска удалено, список результатов пуст. Тем не менее, я смог получить эту работу, после нескольких попыток я получил ConcurrentModificationException, выброшенное самим компонентом. Исходный список, который я ищу, исходит из файла свойств (потому что я не хочу, чтобы поиск ударял базу данных каждый раз, когда пользователь что-то набирает). Я несколько месяцев бил головой об этом. Что мне не хватает? Позвольте мне показать вам, что я сделал:ConcurrentModificationException с использованием Richfaces ExtendedDataTable

Это текст ввода, который должен запускать поисковую логику (я удостоверяю, что таблица не обновляется, когда значение меньше 4 символов или если пользователь нажимает клавиши , как стрелы, сдвиг и 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 на сервер. Используйте атрибут «eventsQueue» в поддержке a4j:. Обычно вы всегда должны использовать «eventsQueue» в любом компоненте ajax, причем все «eventsQueue» на одной странице ссылаются на одну и ту же очередь, если у вас нет веских оснований не делать этого.

Кроме того, вы, вероятно, захотите изучить другой атрибут ajax: «ajaxSingle».

+0

Спасибо, это именно то, что мне нужно. Раньше я использовал ajaxSingle, просто не думал об этом. EventsQueue, однако, был для меня новичком. Оказалось, что так просто. Еще раз спасибо, это действительно помогло. –