Hello, everyone. I apologize for being wordy, but I really need your help. In our project we have a seamless search (I hope it's the right term ;)). That is, a user types the value in a text box and while he types, the table gets updated. I was able to get that working, using Richfaces 3.3.3 component rich:extendedDataTable, however, after a few tries I get ConcurrentModificationException, thrown by the component itself. The initial list I am searching comes from the properties file (because I don't want the search to hit database every time the user types something). I've been banging my head over it for months. What am I missing? Let me show you what I've done:
This is the input text that should trigger the search logic (I make sure that the table does not get updated when the value is less than 4 characters or if the user presses keys, like arrows, shift, and ctrl - this function is "returnunicode(event)"):
<h:inputText id="firmname" value="#{ExtendedTable.searchValue}" >
<a4j:support reRender="resultsTable"
onsubmit="if ((this.value.length<4 && this.value.length>0) || !returnunicode(event)) {
return false;
}"
actionListener="#{ExtendedTable.searchForResults}" event="onkeyup" />
</h:inputText>
Action listener is what should update the list. Here is the extendedDataTable, right below the inputText:
<rich:extendedDataTable selection="#{ExtendedTable.selection}"
tableState="#{ExtendedTable.tableState}" var="item" id="resultsTable" value="#{ExtendedTable.dataModel}">
... <%-- I'm listing columns here --%>
</rich:extendedDataTable>
Now, if it's ok, I would like to show you the back-end code. I only left the logic that's important to my issue.
//The action listener itself
public void searchForResult(ActionEvent e) {
//Results is an ArrayList - a local variable, defined as an empty ArrayList, with getter and setter
synchronized(results) {
results.clear();
}
if (this.searchValue.length() > 3) {
results.clear();
updateTableList();
} else {
results.clear();
}
dataModel = null; // to force the dataModel to be updated.
}
public void updateTableList() {
try {
//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
List<ResultObject> searchedResults = getResultsPerValue(this.searchValue);
synchronized(results) {
results.clear();
results.addAll(Collections.unmodifiableList(searchedResults));
}
} catch(Throwable xcpt) {
lgr.error(TM, xcpt);
}
lgr.debug("Exit");
}
//And finally, a dataModel getter, which is what causes the list to constantly get updated. DataModel (ExtendedTableDataModel<ResultObject> dataModel)
//is also defined as a local variable, but it doesn't have a setter, only this getter, which should get updated automatically, every time the value is changing
//and the results list is refreshed
public synchronized ExtendedTableDataModel<ResultObject> getDataModel() {
try {
if (dataModel == null) {
//If data model is empty, initialize it and fill it with the results currently in a result list
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 (ConcurrentModificationException cme) {
lgr.error(TM+ "$ExtendedTableDataModel.getItemByKey", cme);
}
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 (ConcurrentModificationException cme) {
return getDataModel();
}
return dataModel;
}
And like I said, it works fine, but either when too many calls are made to the back-end or the user types fast (it's hard to catch exactly when it happens), ConcurrentModificationException is thrown. Synchronization of the
Java code has never been my strongest side, I don't know what would be causing it and, most importantly, how can I go about avoiding it. I'm aware, that Richfaces 4.0 has made a lot of changes to rich:extendedDataTable component, I've heard that this was an issue before and now it's resolved. However, I don't have time to upgrade the whole application to Richfaces 4.0 (it's going to be done in phase 2), this is just the small part of the whole project. Maybe there's a workaround? Or, perhaps, there are other ways to implement similar kind of search, using plain
JSF (provided that they are quick enough to implement). I will appreciate any kind of help or advice on that. I hope the code is understandable enough, but if not, let me know, I'll explain further. Thank you in advance, I really appreciate it.