Cache update exception handling to Table.
Change-Id: I882e78c3a2eb1ceaab484be748b6890ee321c290
*/
private boolean keyMapperReset;
+ private List<Throwable> exceptionsDuringCachePopulation = new ArrayList<Throwable>();
+
/* Table constructors */
/**
setRowCacheInvalidated(true);
markAsDirty();
+ maybeThrowCacheUpdateExceptions();
+
+ }
+
+ private void maybeThrowCacheUpdateExceptions() {
+ if (!exceptionsDuringCachePopulation.isEmpty()) {
+ Throwable[] causes = new Throwable[exceptionsDuringCachePopulation
+ .size()];
+ exceptionsDuringCachePopulation.toArray(causes);
+
+ exceptionsDuringCachePopulation.clear();
+ throw new CacheUpdateException(this,
+ "Error during Table cache update", causes);
+ }
+
+ }
+
+ /**
+ * Exception thrown when one or more exceptions occurred during updating of
+ * the Table cache.
+ * <p>
+ * Contains all exceptions which occurred during the cache update.
+ * </p>
+ *
+ */
+ public static class CacheUpdateException extends RuntimeException {
+ private Throwable[] causes;
+ private Table table;
+
+ public CacheUpdateException(Table table, String message,
+ Throwable[] causes) {
+ super(message);
+ this.table = table;
+ this.causes = causes;
+ }
+
+ /**
+ * Returns the cause(s) for this exception
+ *
+ * @return the exception(s) which caused this exception
+ */
+ public Throwable[] getCauses() {
+ return causes;
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
}
/**
cells[CELL_HEADER][i] = String.valueOf(i + firstIndex + 1);
break;
default:
- cells[CELL_HEADER][i] = getItemCaption(id);
+ try {
+ cells[CELL_HEADER][i] = getItemCaption(id);
+ } catch (Exception e) {
+ exceptionsDuringCachePopulation.add(e);
+ cells[CELL_HEADER][i] = "";
+ }
+ }
+ try {
+ cells[CELL_ICON][i] = getItemIcon(id);
+ } catch (Exception e) {
+ exceptionsDuringCachePopulation.add(e);
+ cells[CELL_ICON][i] = null;
}
- cells[CELL_ICON][i] = getItemIcon(id);
}
GeneratedRow generatedRow = rowGenerator != null ? rowGenerator
boolean isGenerated = isGeneratedRow || isGeneratedColumn;
if (!isGenerated) {
- p = getContainerProperty(id, colids[j]);
+ try {
+ p = getContainerProperty(id, colids[j]);
+ } catch (Exception e) {
+ exceptionsDuringCachePopulation.add(e);
+ value = null;
+ }
}
if (isGeneratedRow) {
if (isGeneratedColumn) {
ColumnGenerator cg = columnGenerators
.get(colids[j]);
- value = cg.generateCell(this, id, colids[j]);
+ try {
+ value = cg.generateCell(this, id, colids[j]);
+ } catch (Exception e) {
+ exceptionsDuringCachePopulation.add(e);
+ value = null;
+ }
if (value != null && !(value instanceof Component)
&& !(value instanceof String)) {
// Avoid errors if a generator returns
value = value.toString();
}
} else if (iscomponent[j]) {
- value = p.getValue();
+ try {
+ value = p.getValue();
+ } catch (Exception e) {
+ exceptionsDuringCachePopulation.add(e);
+ value = null;
+ }
listenProperty(p, oldListenedProperties);
} else if (p != null) {
- value = getPropertyValue(id, colids[j], p);
+ try {
+ value = getPropertyValue(id, colids[j], p);
+ } catch (Exception e) {
+ exceptionsDuringCachePopulation.add(e);
+ value = null;
+ }
/*
* If returned value is Component (via fieldfactory
* or overridden getPropertyValue) we expect it to
listenProperty(p, oldListenedProperties);
}
} else {
- value = getPropertyValue(id, colids[j], null);
+ try {
+ value = getPropertyValue(id, colids[j], null);
+ } catch (Exception e) {
+ exceptionsDuringCachePopulation.add(e);
+ value = null;
+ }
}
}
}
indexInRowbuffer, itemId);
}
target.endTag("urows");
+ maybeThrowCacheUpdateExceptions();
}
private void paintPartialRowAdditions(PaintTarget target,
target.addAttribute("firstprowix", firstIx);
target.addAttribute("numprows", count);
target.endTag("prows");
+ maybeThrowCacheUpdateExceptions();
}
/**
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://localhost:9999/" />
+<title>TableWithBrokenGeneratorAndContainer</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">TableWithBrokenGeneratorAndContainer</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.components.table.TableWithBrokenGeneratorAndContainer?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>item1/prop1</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td>
+ <td>item2/prop1</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]</td>
+ <td>item3/prop1</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[4]/domChild[0]</td>
+ <td>Generated item1/Gen</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[4]/domChild[0]</td>
+ <td>Generated item2/Gen</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[4]/domChild[0]</td>
+ <td>Generated item3/Gen</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VCheckBox[0]/domChild[0]</td>
+ <td>12,6</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<!--error indicator should be present-->
+<tr>
+ <td>assertCSSClass</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td>v-errorindicator</td>
+</tr>
+<!--error notification should not be present-->
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td>
+ <td>item2/prop1</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]</td>
+ <td>item3/prop1</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[4]/domChild[0]</td>
+ <td>Generated item1/Gen</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[4]/domChild[0]</td>
+ <td>Generated item2/Gen</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[4]/domChild[0]</td>
+ <td>Generated item3/Gen</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VCheckBox[0]/domChild[0]</td>
+ <td>9,6</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[1]</td>
+ <td></td>
+</tr>
+<!--error indicator should be present-->
+<tr>
+ <td>assertCSSClass</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td>v-errorindicator</td>
+</tr>
+<!--error notification should not be present-->
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td>
+ <td>item2/prop1</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]</td>
+ <td>item3/prop1</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[4]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[4]/domChild[0]</td>
+ <td>Generated item2/Gen</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[4]/domChild[0]</td>
+ <td>Generated item3/Gen</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VCheckBox[0]/domChild[0]</td>
+ <td>9,8</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[1]</td>
+ <td></td>
+</tr>
+<!--error notification-->
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>Problem updating table. Please try again later</td>
+</tr>
+<!--table should be empty-->
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
--- /dev/null
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.table;
+
+import java.lang.reflect.InvocationTargetException;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.event.ListenerMethod.MethodException;
+import com.vaadin.server.ErrorEvent;
+import com.vaadin.server.ErrorHandler;
+import com.vaadin.server.ServerRpcManager.RpcInvocationException;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Table.CacheUpdateException;
+import com.vaadin.ui.Table.ColumnGenerator;
+
+public class TableWithBrokenGeneratorAndContainer extends TestBase {
+
+ private CheckBox brokenContainer = new CheckBox("Broken container");
+ private CheckBox brokenGenerator = new CheckBox("Broken generator");
+ private CheckBox clearTableOnError = new CheckBox("Clear Table on Error");
+
+ /**
+ * Container which throws an exception on every fifth call to
+ * {@link #getContainerProperty(Object, Object)}.
+ *
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0
+ *
+ */
+ public class BrokenContainer extends IndexedContainer {
+ private int counter = 0;
+
+ public BrokenContainer() {
+ super();
+ }
+
+ @Override
+ public Property getContainerProperty(Object itemId, Object propertyId) {
+ if (counter++ % 5 == 0
+ && Boolean.TRUE.equals(brokenContainer.getValue())) {
+ throw new RuntimeException(getClass().getSimpleName()
+ + " cannot fetch the property for " + itemId + "/"
+ + propertyId + " right now");
+ }
+ return super.getContainerProperty(itemId, propertyId);
+ }
+ }
+
+ public class BrokenColumnGenerator implements ColumnGenerator {
+ private int brokenInterval;
+ private int counter = 0;
+
+ public BrokenColumnGenerator(int brokenInterval) {
+ this.brokenInterval = brokenInterval;
+ }
+
+ public Object generateCell(Table source, Object itemId, Object columnId) {
+ if (counter++ % brokenInterval == 0
+ && Boolean.TRUE.equals(brokenGenerator.getValue())) {
+ throw new IllegalArgumentException("Broken generator for "
+ + itemId + "/" + columnId);
+ } else {
+ return "Generated " + itemId + "/" + columnId;
+ }
+ }
+
+ }
+
+ @Override
+ protected void setup() {
+ brokenContainer.setImmediate(true);
+ brokenGenerator.setImmediate(true);
+ clearTableOnError.setImmediate(true);
+ clearTableOnError.addValueChangeListener(new ValueChangeListener() {
+
+ public void valueChange(ValueChangeEvent event) {
+ Boolean value = clearTableOnError.getValue();
+ setErrorHandler(value != null ? value : false);
+ }
+ });
+ final Table table = new Table("Semi-broken table");
+ table.setContainerDataSource(createBrokenContainer(10, 4));
+ table.addGeneratedColumn("Gen", new BrokenColumnGenerator(4));
+ table.setPageLength(20);
+
+ Button refreshTableCache = new Button("Refresh table cache",
+ new Button.ClickListener() {
+
+ public void buttonClick(ClickEvent event) {
+ table.markAsDirty();
+ table.refreshRowCache();
+ }
+ });
+ addComponent(refreshTableCache);
+ addComponent(brokenContainer);
+ addComponent(brokenGenerator);
+ addComponent(clearTableOnError);
+ addComponent(table);
+ }
+
+ protected void setErrorHandler(boolean enabled) {
+ if (enabled) {
+ VaadinSession.getCurrent().setErrorHandler(new ErrorHandler() {
+
+ @Override
+ public void error(ErrorEvent event) {
+ Throwable t = event.getThrowable();
+ if (t instanceof RpcInvocationException) {
+ t = t.getCause();
+ if (t instanceof InvocationTargetException) {
+ t = t.getCause();
+ if (t instanceof MethodException) {
+ t = t.getCause();
+ if (t instanceof CacheUpdateException) {
+ Table table = ((CacheUpdateException) t)
+ .getTable();
+ table.removeAllItems();
+ Notification
+ .show("Problem updating table. Please try again later",
+ Notification.Type.ERROR_MESSAGE);
+ }
+ }
+ }
+ }
+ }
+ });
+ } else {
+ VaadinSession.getCurrent().setErrorHandler(this);
+ }
+ }
+
+ private BrokenContainer createBrokenContainer(int rows, int cols) {
+ BrokenContainer container = new BrokenContainer();
+ for (int j = 1; j <= cols; j++) {
+ container.addContainerProperty("prop" + j, String.class, null);
+ }
+ for (int i = 1; i <= rows; i++) {
+ Item item = container.addItem("item" + i);
+ for (int j = 1; j <= cols; j++) {
+ item.getItemProperty("prop" + j).setValue(
+ "item" + i + "/prop" + j);
+ }
+ }
+ return container;
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 10312;
+ }
+
+ @Override
+ protected String getDescription() {
+ return "A Table should not show 'Internal Error' just because a column generator or container throws an exception during filling of the cache";
+ }
+
+}