diff options
author | patrik <patrik@vaadin.com> | 2015-08-03 11:41:03 +0300 |
---|---|---|
committer | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-08-20 09:43:23 +0000 |
commit | 7ed0d2a10e250b6ccadca1a6a1d3b9e52b9973b7 (patch) | |
tree | b043b887e9eef43292ab9adf97b31f7446a56b25 | |
parent | 2a26dc1d12ea9c201569e45148626220103ec980 (diff) | |
download | vaadin-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.java | 36 | ||||
-rw-r--r-- | uitest/src/com/vaadin/tests/components/grid/GridFastAsyncUpdate.java | 148 |
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 |