summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpatrik <patrik@vaadin.com>2015-08-03 11:41:03 +0300
committerTeemu Suo-Anttila <teemusa@vaadin.com>2015-08-20 09:43:23 +0000
commit7ed0d2a10e250b6ccadca1a6a1d3b9e52b9973b7 (patch)
treeb043b887e9eef43292ab9adf97b31f7446a56b25
parent2a26dc1d12ea9c201569e45148626220103ec980 (diff)
downloadvaadin-framework-7ed0d2a10e250b6ccadca1a6a1d3b9e52b9973b7.tar.gz
vaadin-framework-7ed0d2a10e250b6ccadca1a6a1d3b9e52b9973b7.zip
Prevent race-condition induced sporadic error in Escalator (#17258)
Change-Id: I624c44056d43a6a3205be46a748070f269a3c3e3
-rw-r--r--client/src/com/vaadin/client/widgets/Escalator.java36
-rw-r--r--uitest/src/com/vaadin/tests/components/grid/GridFastAsyncUpdate.java148
2 files changed, 174 insertions, 10 deletions
diff --git a/client/src/com/vaadin/client/widgets/Escalator.java b/client/src/com/vaadin/client/widgets/Escalator.java
index 5b3d4e1b70..7b0bb3bfe4 100644
--- a/client/src/com/vaadin/client/widgets/Escalator.java
+++ b/client/src/com/vaadin/client/widgets/Escalator.java
@@ -2767,17 +2767,33 @@ public class Escalator extends Widget implements RequiresResize,
// move the surrounding rows to their correct places.
double rowTop = (unupdatedLogicalStart + (end - start))
* getDefaultRowHeight();
- final ListIterator<TableRowElement> i = visualRowOrder
- .listIterator(visualTargetIndex + (end - start));
- int logicalRowIndexCursor = unupdatedLogicalStart;
- while (i.hasNext()) {
- rowTop += spacerContainer
- .getSpacerHeight(logicalRowIndexCursor++);
-
- final TableRowElement tr = i.next();
- setRowPosition(tr, 0, rowTop);
- rowTop += getDefaultRowHeight();
+ // TODO: Get rid of this try/catch block by fixing the
+ // underlying issue. The reason for this erroneous behavior
+ // might be that Escalator actually works 'by mistake', and
+ // the order of operations is, in fact, wrong.
+ try {
+ final ListIterator<TableRowElement> i = visualRowOrder
+ .listIterator(visualTargetIndex + (end - start));
+
+ int logicalRowIndexCursor = unupdatedLogicalStart;
+ while (i.hasNext()) {
+ rowTop += spacerContainer
+ .getSpacerHeight(logicalRowIndexCursor++);
+
+ final TableRowElement tr = i.next();
+ setRowPosition(tr, 0, rowTop);
+ rowTop += getDefaultRowHeight();
+ }
+ } catch (Exception e) {
+ Logger logger = getLogger();
+ logger.warning("Ignored out-of-bounds row element access");
+ logger.warning("Escalator state: start=" + start
+ + ", end=" + end + ", visualTargetIndex="
+ + visualTargetIndex
+ + ", visualRowOrder.size()="
+ + visualRowOrder.size());
+ logger.warning(e.toString());
}
}
diff --git a/uitest/src/com/vaadin/tests/components/grid/GridFastAsyncUpdate.java b/uitest/src/com/vaadin/tests/components/grid/GridFastAsyncUpdate.java
new file mode 100644
index 0000000000..31fe0275a5
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/grid/GridFastAsyncUpdate.java
@@ -0,0 +1,148 @@
+package com.vaadin.tests.components.grid;
+
+import java.util.Calendar;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+
+import com.vaadin.annotations.Push;
+import com.vaadin.annotations.Theme;
+import com.vaadin.data.Item;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.event.SelectionEvent;
+import com.vaadin.event.SelectionEvent.SelectionListener;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.Grid.SelectionMode;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.VerticalLayout;
+
+@Push
+@Theme("valo")
+@SuppressWarnings("serial")
+public class GridFastAsyncUpdate extends AbstractTestUI {
+
+ private final Runnable addRowsTask = new Runnable() {
+ @Override
+ public void run() {
+ System.out.println("Logging...");
+ try {
+ Random random = new Random();
+ while (!Thread.currentThread().isInterrupted()) {
+ Thread.sleep(random.nextInt(100));
+
+ GridFastAsyncUpdate.this.access(new Runnable() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+
+ ++counter;
+ Item item = container.addItem(counter);
+ item.getItemProperty("sequenceNumber").setValue(
+ String.valueOf(counter));
+ item.getItemProperty("millis").setValue(
+ String.valueOf(Calendar.getInstance()
+ .getTimeInMillis() - loggingStart));
+ item.getItemProperty("level").setValue(
+ Level.INFO.toString());
+ item.getItemProperty("message").setValue("Message");
+ if (grid != null && !scrollLock) {
+ grid.scrollToEnd();
+ }
+ }
+ });
+ }
+ } catch (InterruptedException e) {
+ System.out.println("logging thread interrupted");
+ }
+ }
+ };
+
+ private int counter;
+
+ private Grid grid;
+ private IndexedContainer container;
+ private long loggingStart;
+ private volatile boolean scrollLock = false;
+
+ @Override
+ protected void setup(VaadinRequest vaadinRequest) {
+ final VerticalLayout layout = new VerticalLayout();
+ layout.setSizeFull();
+ layout.setMargin(true);
+ addComponent(layout);
+
+ HorizontalLayout buttons = new HorizontalLayout();
+ layout.addComponent(buttons);
+
+ final ExecutorService logExecutor = Executors.newSingleThreadExecutor();
+
+ final Button logButton = new Button("Start logging");
+ logButton.addClickListener(new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ if ("Start logging".equals(logButton.getCaption())) {
+ loggingStart = Calendar.getInstance().getTimeInMillis();
+ logExecutor.submit(addRowsTask);
+ logButton.setCaption("Stop logging");
+ } else {
+ System.out.println("Stop logging...");
+ try {
+ logExecutor.shutdownNow();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ logButton.setCaption("Start logging");
+ }
+ }
+ });
+ buttons.addComponent(logButton);
+
+ final Button scrollButton = new Button("Stop scrolling");
+ scrollButton.addClickListener(new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ if (!scrollLock) {
+ System.out.println("Stop scrolling");
+ scrollButton.setCaption("Start scrolling");
+ scrollLock = true;
+ } else {
+ System.out.println("Start scrolling");
+ scrollButton.setCaption("Stop scrolling");
+ scrollLock = false;
+ }
+ }
+ });
+ buttons.addComponent(scrollButton);
+
+ container = new IndexedContainer();
+ container.addContainerProperty("sequenceNumber", String.class, null);
+ container.addContainerProperty("millis", String.class, null);
+ container.addContainerProperty("level", String.class, null);
+ container.addContainerProperty("message", String.class, null);
+
+ grid = new Grid(container);
+ grid.setWidth("100%");
+ grid.setImmediate(true);
+ grid.setSelectionMode(SelectionMode.SINGLE);
+ grid.addSelectionListener(new SelectionListener() {
+ @Override
+ public void select(final SelectionEvent event) {
+ if (grid.getSelectedRow() != null) {
+ disableScroll();
+ }
+ }
+ });
+
+ layout.addComponent(grid);
+ layout.setExpandRatio(grid, 1.0f);
+ }
+
+ protected void disableScroll() {
+ scrollLock = true;
+ }
+} \ No newline at end of file