From 7c5aec1e34616d89726252f36c9650084c871ab9 Mon Sep 17 00:00:00 2001 From: Anna Koskinen Date: Tue, 29 Jan 2013 16:35:27 +0200 Subject: [PATCH] Merge of (#10312) to Vaadin 7. Cache update exception handling to Table. Change-Id: I882e78c3a2eb1ceaab484be748b6890ee321c290 --- server/src/com/vaadin/ui/Table.java | 102 +++++++++- .../TableWithBrokenGeneratorAndContainer.html | 178 +++++++++++++++++ .../TableWithBrokenGeneratorAndContainer.java | 181 ++++++++++++++++++ 3 files changed, 454 insertions(+), 7 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html create mode 100644 uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index e73c6d7188..10752140db 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -557,6 +557,8 @@ public class Table extends AbstractSelect implements Action.Container, */ private boolean keyMapperReset; + private List exceptionsDuringCachePopulation = new ArrayList(); + /* Table constructors */ /** @@ -1646,6 +1648,55 @@ public class Table extends AbstractSelect implements Action.Container, 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. + *

+ * Contains all exceptions which occurred during the cache update. + *

+ * + */ + 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; + } + } /** @@ -2036,9 +2087,19 @@ public class Table extends AbstractSelect implements Action.Container, 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 @@ -2056,7 +2117,12 @@ public class Table extends AbstractSelect implements Action.Container, 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) { @@ -2089,7 +2155,12 @@ public class Table extends AbstractSelect implements Action.Container, 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 @@ -2098,10 +2169,20 @@ public class Table extends AbstractSelect implements Action.Container, 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 @@ -2114,7 +2195,12 @@ public class Table extends AbstractSelect implements Action.Container, 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; + } } } } @@ -3049,6 +3135,7 @@ public class Table extends AbstractSelect implements Action.Container, indexInRowbuffer, itemId); } target.endTag("urows"); + maybeThrowCacheUpdateExceptions(); } private void paintPartialRowAdditions(PaintTarget target, @@ -3096,6 +3183,7 @@ public class Table extends AbstractSelect implements Action.Container, target.addAttribute("firstprowix", firstIx); target.addAttribute("numprows", count); target.endTag("prows"); + maybeThrowCacheUpdateExceptions(); } /** diff --git a/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html new file mode 100644 index 0000000000..c2481e6be6 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html @@ -0,0 +1,178 @@ + + + + + + +TableWithBrokenGeneratorAndContainer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TableWithBrokenGeneratorAndContainer
open/run/com.vaadin.tests.components.table.TableWithBrokenGeneratorAndContainer?restartApplication
assertTextvaadin=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]item1/prop1
assertTextvaadin=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]item2/prop1
assertTextvaadin=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]item3/prop1
assertTextvaadin=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]Generated item1/Gen
assertTextvaadin=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]Generated item2/Gen
assertTextvaadin=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]Generated item3/Gen
mouseClickvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VCheckBox[0]/domChild[0]12,6
clickvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]
assertCSSClassvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]v-errorindicator
assertElementNotPresentvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]
assertTextvaadin=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]
assertTextvaadin=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]item2/prop1
assertTextvaadin=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]item3/prop1
assertTextvaadin=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]Generated item1/Gen
assertTextvaadin=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]Generated item2/Gen
assertTextvaadin=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]Generated item3/Gen
mouseClickvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VCheckBox[0]/domChild[0]9,6
clickvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[1]
assertCSSClassvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]v-errorindicator
assertElementNotPresentvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]
assertTextvaadin=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]
assertTextvaadin=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]item2/prop1
assertTextvaadin=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]item3/prop1
assertTextvaadin=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]
assertTextvaadin=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]Generated item2/Gen
assertTextvaadin=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]Generated item3/Gen
mouseClickvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VCheckBox[0]/domChild[0]9,8
clickvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[1]
assertTextvaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]/HTML[0]/domChild[0]Problem updating table. Please try again later
assertElementNotPresentvaadin=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]
+ + diff --git a/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java new file mode 100644 index 0000000000..9c5ce9dc0c --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java @@ -0,0 +1,181 @@ +/* + * 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"; + } + +} -- 2.39.5