ArrayIndexOutOfBounds in com.vaadin.ui.Table.paintRows(Table.java:3064)

Does anybody know if the ticket #7836 has been resolved yet (
ticket #7836
)?
Ive got exactly the same problem (vaadin 6.7.4) - when a table performs lazy loading after its scrolling, all of a sudden (pretty often, especially if I scroll down and then up) I get this ArrayIndexOutOfBounds exception risen in com.vaadin.ui.Table.paintRows(Table.java:3064). Its totally intolerable because table control is one of the main features on my page.
If anybody knows any workaround, I would deeply appreciate!

As it says in the ticket, it’s closed after a possible fix. It’s of course possible that the fix wasn’t enough in which case it would be necessary to reopen the ticket. If that is the case, it would be great if you could provide a stripped-down test case that reproduces the problem.

Does your problem occur with the same FilesystemContainer? Some errors that occur during component paint can be caused by something else, such as a corrupted data source.

Hi Marko! Thank you for response!
No, Im recently using SQLContainer but the sympthoms are exactly the same as described in the ticket - when there is a little number of rows in the table, all works fine. Once you gotta scroll through the datasource and fetch more (Im using lazy loading and debugging didnt show anything wrong) you get this error after some while of scrolling up and down. In case you are interested, I can provide you with the source code for the FreeformStatementDelegate implementation…

Heres where the error occurs:


private void paintRows(PaintTarget target, final Object[][]
 cells,
            final Set<Action> actionSet) throws PaintException {
        final boolean[] iscomponent = findCellsWithComponents();

        target.startTag("rows");
        // cells array contains all that are supposed to be visible on client,
        // but we'll start from the one requested by client
        int start = 0;
        if (reqFirstRowToPaint != -1 && firstToBeRenderedInClient != -1) {
            start = reqFirstRowToPaint - firstToBeRenderedInClient;
        }
        int end = cells[0]
.length;
        if (reqRowsToPaint != -1) {
            end = start + reqRowsToPaint;
        }
        // sanity check
        if (lastToBeRenderedInClient != -1 && lastToBeRenderedInClient < end) {
            end = lastToBeRenderedInClient + 1;
        }
        if (start > cells[CELL_ITEMID]
.length || start < 0) {
            start = 0;
        }

        for (int indexInRowbuffer = start; indexInRowbuffer < end; indexInRowbuffer++) {
            [color=#fc0a0a]
final Object itemId = cells[CELL_ITEMID]
[indexInRowbuffer]
;
[/color]

            if (shouldHideNullSelectionItem()) {
                // Remove null selection item if null selection is not allowed
                continue;
            }

            paintRow(target, cells, isEditable(), actionSet, iscomponent,
                    indexInRowbuffer, itemId);
        }
        target.endTag("rows");
    }


THIS
seems to be the remedy (though the bug still exists in the core component)

The issue in the ticket was specifically for FilesystemContainer so this is probably a separate issue.

The use of FreeformQueryDelegate makes me really worry that the issue could be in your implementation or somewhere else in your code. Calculating the number of table rows badly in getCountQuery() - or something similar - would result in wrong number of rows and the Table would certainly barf exactly the way it does for you.

For example, if you use filters, you may have forgotten to include the filter statement somewhere in the implementation, thus getting wrong number of rows. Or, the number of rows could have changed during painting somehow. There are many possibilities and the mentioned error in Table is probably just a symptom for a problem in the container.

It’s possible that PagedTable isn’t as picky about the number of rows. For example, Table buffers some rows outside the visible view while PagedTable probably doesn’t.

I agree with you that there might be some problems with the delegate implementation and Im going to get down to tracing it more regorously yet. Still, you have to also agree that in case if the bounds of the range had been checked better in the Table class, there wouldnt have been any hassle like this :wink:
Also, the exception gets risen even if I dont use any filter, so only “plain” sql is being used (of course I should note that I have to retreive only a range of records to realize the lazy loading).
Anyway, I better post the code of my implementation class in hope, you might help me find a bug if there is any.
.[code]

public class TableStatementDelegate implements FreeformStatementDelegate {
private static final long serialVersionUID = 1L;
private List filters;
private List orderBys;
private final String tableName;
private final String idFieldName;
private List fields;

public TableStatementDelegate(String tableName, String idFieldName,
		List<String> fields) {
	super();
	this.tableName = tableName;
	this.idFieldName = idFieldName;
	this.fields = fields;
}

public String getQueryString(int offset, int limit)
		throws UnsupportedOperationException {
	throw new UnsupportedOperationException("Use getQueryStatement method.");
}

private String getOrderByString() {
	StringBuffer orderBuffer = new StringBuffer("");
	if (orderBys != null && !orderBys.isEmpty()) {
		orderBuffer.append(" ORDER BY ");
		OrderBy lastOrderBy = orderBys.get(orderBys.size() - 1);
		for (OrderBy orderBy : orderBys) {
			orderBuffer.append(orderBy.getColumn());
			if (orderBy.isAscending()) {
				orderBuffer.append(" ASC");
			} else {
				orderBuffer.append(" DESC");
			}
			if (orderBy != lastOrderBy) {
				orderBuffer.append(", ");
			}
		}
	}
	return orderBuffer.toString();
}

private String getFiltersString() {
	StringBuffer filterBuffer = new StringBuffer("");
	System.out.println("dick1");
	if (filters != null && !filters.isEmpty()) {
		System.out.println("dick2");
		Filter lastFilter = filters.get(filters.size() - 1);
		filterBuffer.append(" WHERE ");
		for (Filter filter : filters) {
			filterBuffer.append(filter.toString());
			if (filter != lastFilter) {
				filterBuffer.append(" AND ");
			}
		}
	}
	return filterBuffer.toString();
}

public String getCountQuery() throws UnsupportedOperationException {
	throw new UnsupportedOperationException("Use getCountStatement method.");
}

public void setFilters(List<Filter> filters)
		throws UnsupportedOperationException {
	this.filters = filters;
}

public void setOrderBy(List<OrderBy> orderBys)
		throws UnsupportedOperationException {
	this.orderBys = orderBys;
}

public int storeRow(Connection conn, RowItem row)
		throws UnsupportedOperationException, SQLException {
	Statement statement = conn.createStatement();

	String query = null;
	if (row.getId() instanceof TemporaryRowId) {
		query = getInsertQuery(row);
	} else {
		query = getUpdateQuery(row);
	}

	int retval = statement.executeUpdate(query);
	statement.close();
	return retval;
}

private String getInsertQuery(RowItem row) {
	StringBuffer insert = new StringBuffer("INSERT INTO " + tableName + "(");
	String lastField = fields.get(fields.size() - 1);
	for (String field : fields) {
		insert.append(field);
		if (field != lastField) {
			insert.append(", ");
		}
	}
	insert.append(") VALUES (");
	for (String field : fields) {
		appendInsertValue(insert, row, field);
		if (field != lastField) {
			insert.append(", ");
		}
	}
	insert.append(")");
	return insert.toString();
}

private void appendInsertValue(StringBuffer insert, RowItem row,
		String propId) {
	Object val = row.getItemProperty(propId).getValue();
	if (val != null) {
		if (val instanceof String) {
			insert.append("'").append(val).append("'");
		} else if (val instanceof Date) {
			SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy");
			insert.append("TO_DATE('").append(df.format(val))
					.append("', 'dd.mm.yyyy')");
		} else {
			insert.append(val);
		}
	} else {
		insert.append("NULL");
	}
}

private String getUpdateQuery(RowItem row) {
	StringBuffer update = new StringBuffer("UPDATE " + tableName + " SET ");
	String lastField = fields.get(fields.size() - 1);
	for (String field : fields) {
		appendUpdateValue(update, row, field);
		if (field != lastField) {
			update.append(", ");
		}
	}
	update.append(" WHERE " + idFieldName + " = ").append(
			row.getItemProperty(idFieldName));
	return update.toString();
}

private void appendUpdateValue(StringBuffer update, RowItem row,
		String propId) {
	update.append(propId).append(" = ");
	Object val = row.getItemProperty(propId).getValue();
	if (val != null) {
		if (val instanceof String) {
			update.append("'").append(val).append("'");
		} else if (val instanceof Date) {
			SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy");
			update.append("TO_DATE('").append(df.format(val))
					.append("', 'dd.mm.yyyy')");
		} else {
			update.append(val);
		}
	} else {
		update.append("NULL");
	}
}

public boolean removeRow(Connection conn, RowItem row)
		throws UnsupportedOperationException, SQLException {
	Statement statement = conn.createStatement();
	int rowsChanged = statement.executeUpdate("DELETE FROM " + tableName
			+ " WHERE " + idFieldName + " = "
			+ row.getItemProperty(idFieldName));
	statement.close();
	return rowsChanged == 1;
}

public String getContainsRowQueryString(Object... keys)
		throws UnsupportedOperationException {
	throw new UnsupportedOperationException(
			"Please use getContainsRowQueryStatement method.");
}

public StatementHelper getQueryStatement(int offset, int limit)
		throws UnsupportedOperationException {
	StatementHelper result = new StatementHelper();
	StringBuffer query = new StringBuffer("SELECT ");
	String fieldString = "";
	String lastField = fields.get(fields.size() - 1);
	for (String field : fields) {
		query.append(field);
		fieldString += field;
		if (field != lastField) {
			query.append(", ");
			fieldString += ", ";
		}
	}
	query.append(" FROM ");
	query.append(tableName + " ");
	if (filters != null) {
		// query.append(QueryBuilder.getWhereStringForFilters(filters,
		// result));
		query.append(getFiltersString());
	}
	query.append(getOrderByString());
	if (offset != 0 || limit != 0) {
		query.insert(0, "select " + fieldString
				+ " from ( select a.*, rownum r from ( ");
		query.append(" ) a  where rownum < " + (offset + limit)
				+ " ) where r > " + offset);
	}
	result.setQueryString(query.toString());
	System.out.println(query.toString());
	return result;
}

public StatementHelper getCountStatement()
		throws UnsupportedOperationException {
	StatementHelper result = new StatementHelper();
	StringBuffer query = new StringBuffer("SELECT COUNT(*) FROM "
			+ tableName + " ");
	if (filters != null) {
		// query.append(QueryBuilder.getWhereStringForFilters(filters,
		// result));
		query.append(getFiltersString());
	}
	result.setQueryString(query.toString());
	System.out.println(query.toString());
	return result;
}

public StatementHelper getContainsRowQueryStatement(Object... keys)
		throws UnsupportedOperationException {
	StatementHelper result = new StatementHelper();
	StringBuffer query = new StringBuffer("SELECT ");
	String lastField = fields.get(fields.size() - 1);
	for (String field : fields) {
		query.append(field);
		if (field != lastField) {
			query.append(", ");
		}
	}
	query.append(" FROM " + tableName + " WHERE " + idFieldName + " = ?");
	result.addParameterValue(keys[0]

);
result.setQueryString(query.toString());
return result;
}

}

[/code]

The getQueryStatement() might be the most probable cause if the problem occurs also early in the table. Perhaps the query results in wrong number of rows - perhaps the limit is handled wrong or something. The code with two levels of subqueries looks a bit complexish so you should check that it really works.

Well, the error indicates a wrong number of rows so it might be something related to that.

I’m having this same problem with ContainerHierarchicalWrapper and TreeTable.

The collection being wrapped contains “recording items” which contain recordings and recording groups.

If I don’t bother setting the parents the table displays fine. If I do set the parents I get

SEVERE: Terminal error:
java.lang.ArrayIndexOutOfBoundsException: 1
	at com.vaadin.ui.Table.paintRows(Table.java:3064)

Here is the ContainerHierarchicalWrapper


public class RecordingContainer extends ContainerHierarchicalWrapper implements UIComponent {

	private static final long serialVersionUID = 1L;
	protected transient Blackboard blackboard;
	
	protected transient final Logger LOGGER = Logger.getLogger(this.getClass());
	
	public RecordingContainer(Container toBeWrapped) {
		super(toBeWrapped);
		setUpRecordingGroups();
	}
	
	private void setUpRecordingGroups(){
		Collection<?> idList = this.getItemIds();
		for(Object o : idList){
			Object obj = this.getItem(o);
			BeanItem item =(BeanItem)obj;
			if (item.getBean() instanceof Recording){
				this.setChildrenAllowed(o, false);
				Recording rec =(Recording)item.getBean();
				LOGGER.debug("examining found recording item "+rec.getName());
				RecordingGroup g = rec.getGroup();
				if (idList.contains(g)){
					LOGGER.debug("list contians group id, setting parent group "+g.getName());
					boolean set = this.setParent(o, g);
					LOGGER.debug("parent set " + set);
				}
			}
		}
	}

should I being starting a new ticket in trac for this? looks like a similar bug was closed(cantfix)

http://dev.vaadin.com/ticket/8273#comment:12

updating vaadin from 6.7.4 to 6.7.6 makes the page hang with an infinite restart of the application.

Note that this is a little different of a bug since

  1. the container is different
  2. the number of records is not high (12 in this particular example)

Cheers,
Matt

Decided to try a more “vanilla” approach to rule out a few things and it seems that the following method sets up the container fine


	private void setUpRecordingGroups2(){
		Collection<?> idList = this.getItemIds();
		int i =0;
		Object temp =null;
		for(Object o : idList){
			if (i%3!=0){
				this.setParent(o, temp);
				this.setChildrenAllowed(o, false);
			}else{
				temp =o;
			}
			i++;
		}
	}