question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

DataCommunicator gets stuck after browser refresh, eventually causing OutOfMemoryError

See original GitHub issue

Description of the bug

After upgrading to Vaadin 23.1.6, we encountered situations where the DataCommunicator of a Grid gets stuck after a browser refresh. It seems that the DataCommunicator gets stuck in some kind of flush-loop, which ultimately leads to an OutOfMemoryError. I suppose the problem was introduced in flow-data 23.1.4, this change might be relevant: https://github.com/vaadin/flow/commit/c55bdcc6dd9dac75be42291b5752d1d86d5a9997 This bug does not appear after downgrading to Vaadin 23.1.4

Note: In our application, we often use views that have a Grid on the left side and a TabSheet on the right side which can show data about the object that is selected in the left Grid. Most of the time, the data on the right side will be shown with a Grid. This type of view is affected by the problem. In general, it seems that the problem can appear in case that a component is shown, which was already used and removed before the browser refresh.

I created this issue in the flow repository, since the problem is at its core caused by the DataCommunicator.

Expected behavior

After a browser refresh, it should be possible to show a component that contains a Grid and was shown before the browser refresh without causing this bevahiour (this was possible before 23.1.6). The DataCommunicator should not get stuck in an infinite loop.

Minimal reproducible example

Below is an example that can reproduce the bug.

Steps to reproduce the bug: Go to /test Select “1” in left table Click “Show Table 1” Click “Show Table 2” Press F5 Click “Show Table 1”

import java.util.ArrayList;
import java.util.Arrays;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.AfterNavigationEvent;
import com.vaadin.flow.router.AfterNavigationObserver;
import com.vaadin.flow.router.PreserveOnRefresh;
import com.vaadin.flow.router.Route;

@PreserveOnRefresh
@Route("test")
public class TestView extends Div implements AfterNavigationObserver{

	private HorizontalLayout hLayout;
		
	private Div wrapperRight;
	
	private Grid<String> tableLeft;
	
	private Grid<String> tableRightOne;
	
	private Grid<String> tableRightTwo;
	
	private Button buttonShowTableOne;
	
	private Button buttonShowTableTwo;
	
	private Div buttonSlot;
	
	private Div tableSlot;
	
	public TestView() {
		setSizeFull();
		build();
	}
	
	private void build() {
		String value1 = "1";
		String value2 = "2";
		
		hLayout = new HorizontalLayout();
		hLayout.setSizeFull();
		
		Div wrapperLeft = new Div();
		wrapperLeft.setHeightFull();
		wrapperLeft.setWidth("50%");
		hLayout.add(wrapperLeft);
		
		tableLeft = new Grid<>();
		tableLeft.addColumn(e -> e).setFlexGrow(1);
		wrapperLeft.add(tableLeft);
		
		tableLeft.setItems(Arrays.asList(value1, value2));
		
		wrapperRight = new Div();
		wrapperRight.setHeightFull();
		wrapperRight.setWidth("50%");
		hLayout.add(wrapperRight);
		
		buttonSlot = new Div();
		buttonShowTableOne = new Button("Show Table 1");
		buttonSlot.add(buttonShowTableOne);
		buttonShowTableTwo = new Button("Show Table 2");
		buttonSlot.add(buttonShowTableTwo);
		wrapperRight.add(buttonSlot);
		
		buttonShowTableOne.addClickListener(e -> {
			tableSlot.removeAll();
			tableSlot.add(tableRightOne);
		});
		
		buttonShowTableTwo.addClickListener(e -> {
			tableSlot.removeAll();
			tableSlot.add(tableRightTwo);
		});
		
		tableSlot = new Div();
		wrapperRight.add(tableSlot);
				
		tableRightOne = new Grid<>();
		tableRightOne.addColumn( e -> e).setHeader("c1").setFlexGrow(1);
		tableRightOne.addColumn(e -> new Checkbox()).setHeader("c2").setFlexGrow(1);
		tableRightOne.addColumn( e -> e).setHeader("c3").setFlexGrow(1).setVisible(false);
		tableRightOne.setItems(new ArrayList<>(Arrays.asList("1", "2", "3")));
		
		tableRightTwo = new Grid<>();
		tableRightTwo.addColumn( e -> e).setHeader("c1").setFlexGrow(1);
		tableRightTwo.addColumn(e -> new Checkbox()).setHeader("c2").setFlexGrow(1);
		tableRightTwo.addColumn( e -> e).setHeader("c3").setFlexGrow(1).setVisible(false);
		tableRightTwo.setItems(new ArrayList<>(Arrays.asList("4", "5", "6")));
		
		tableLeft.addSelectionListener(e -> {
			refreshRightSide();
		});
	}
	
	private void refreshRightSide() {
		displayNothingSelected();
		if(tableLeft.getSelectedItems().isEmpty()) {
		} else {
			
			if("1".equals(tableLeft.getSelectedItems().iterator().next())) {
				tableRightOne.setItems(new ArrayList<>(Arrays.asList("1", "2", "3")));
			} else {
				tableRightTwo.setItems(new ArrayList<>(Arrays.asList("4", "5", "6")));
			}
		}
	}
	
	private void displayNothingSelected() {
		tableSlot.removeAll();
		tableSlot.add(new Div());
	}
	
	protected void wrapRightSide(Component component) {
		if (!component.isAttached()) {
			Div wrapper = new Div();
			wrapper.setSizeFull();
			wrapper.add(component);
			wrapperRight.removeAll();
			wrapperRight.add(wrapper);
		}
	}

	@Override
	public void afterNavigation(AfterNavigationEvent event) {
		removeAll();
		
		add(hLayout);
		
		refreshRightSide();
	}
	
}

Versions

  • Vaadin / Flow version: 23.1.6
  • Java version: 17
  • OS version: Windows

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
p-englcommented, Sep 26, 2022

The example I provided was simply intended to reproduce the problem we encountered in 23.1.6 in several places.

A more realsitic scenario in our application looks like this:

We have a view with a grid on the left side and a tabsheet on the right side. The grid on the left side contains objects of different types. The data/components which are shown in the tabs on the right side depend on the object that is selected in the left grid. Example: When I select an object of type A in the left grid, I have to show grid X in the first tab of the tabsheet. Grid X contains data about the object that is selected in the left grid. When I select an object of type B in the left grid, I have to show grid Y in the first tab of the tabsheet. Grid Y contains data about the object that is selected in the left grid. This means that everytime I change the selection in the left grid I possibly have the switch the grid that is displayed in the first tab and also have to refresh its content. In the second tab we use another grid to show different data about the object that is selected in the left grid. For the second tab however we can use the same grid regardless of the object type that is selected in the left grid.

This can lead to the same process that you described earlier: Select object of type A in the left grid ->Show grid X in first tab of tabsheet Select object of type B in the left grid ->Show grid Y in first tab of tabsheet (which requires to remove grid X) Press F5 Select object of type A in the left grid ->Show grid X in first tab of tabsheet

Some additional notes: This problem would also arise if we use the same grid in the first tab, because switching the selected tab in the tabsheet also requires removing/adding grids. We reuse the grids for performance and simplicity reasons - we do not want to recreate grids whenever the selection in the left grid changes. We also want to refresh the data on the right side when the user hits F5. The user assumes that the grid in the tabsheet displays the current data after pressing F5.

In any case I’d argue that this is a problem of the DataCommunicator, regardless of whether the provided example makes sense or not.

0reactions
vaadin-botcommented, Oct 10, 2022

This ticket/PR has been released with Vaadin 23.1.11.

Read more comments on GitHub >

github_iconTop Results From Across the Web

HtmlUnit failure: Attempted ImmediateRefreshHandler ...
The full exception is: HtmlUnit: Attempted to refresh a page using an ImmediateRefreshHandler which could have caused an OutOfMemoryError Please use ...
Read more >
2 solution of java.lang.OutOfMemoryError in Java - Javarevisited
OutOfMemoryError in Java is a subclass of java.lang.VirtualMachineError and JVM throws java.lang.OutOfMemoryError when it ran out of memory in the heap.
Read more >
Java OutOfMemoryError Exceptions: Causes & Fixes [Tutorial]
This error means that you tried to keep too much data on the heap of the JVM process and there is not enough...
Read more >
1.17 Update error - Help - Minehut Forums
When I update my server to either paper or spigot 1.17 and try to start the server, it eventually has an error: "java.lang.OutOfMemoryError: ......
Read more >
How to fix out of memory errors by increasing available memory
Java applications like JIRA, Crowd and Confluence run in a "Java virtual machine" (JVM), instead of directly within an operating system. When ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found