flow icon indicating copy to clipboard operation
flow copied to clipboard

Grid without columns failed to serialize

Open davidef opened this issue 2 years ago • 2 comments

Description of the bug

Grid without columns failed to serialize with the following exception: cannot assign instance of java.lang.invoke.SerializedLambda to field com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper.registration of type com.vaadin.flow.shared.Registration in instance of com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper In own production code the same exception happens also with columns configured but we weren't able to create a minimal reproducible example.

Minimal reproducible example

@Route(value = "grid-serialization")
public class GridSerializationIssue extends VerticalLayout {
	private static final long serialVersionUID = 1L;
	
	private final Grid<Currency> grid;

	public GridSerializationIssue() {
		Button test = new Button("serialize", event -> {
			try {
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				try(ObjectOutputStream oos = new ObjectOutputStream(bos)){
					oos.writeObject(GridSerializationIssue.this);
					oos.flush();
				}
				try(ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray())){
					try(ObjectInputStream ois = new ObjectInputStream(bis)){
						GridSerializationIssue readObject = (GridSerializationIssue) ois.readObject();
					}
				}
				Notification.show("OK");
			}catch (Exception e) {
				e.printStackTrace();
				Notification.show("FAILED: " + e.getMessage());
			}
		});
		add(test);

		grid = new Grid<>();
		add(grid);
	}
	
	public static class Currency implements Serializable{
		private static final long serialVersionUID = 1L;
		
		private String code;
		private String currency;
		private String symbol;
		
		public Currency(){}

		public String getCurrency() {
			return currency;
		}

		public void setCurrency(String currency) {
			this.currency = currency;
		}
		
		public String getCode() {
			return code;
		}

		public void setCode(String code) {
			this.code = code;
		}

		public String getSymbol() {
			return symbol;
		}

		public void setSymbol(String symbol) {
			this.symbol = symbol;
		}
	}
}

Expected behavior

No exception throwed on serialization

Versions:

- Vaadin / Flow version: 23.2.2
- Java version: 17
- OS version: osx/linux
- Browser version (if applicable): -
- Application Server (if applicable): both tomcat and springboot
- IDE (if applicable): -

davidef avatar Sep 27 '22 10:09 davidef

We managed to reproduce with a simple example also with the columns configured. We also find a even weird case were the first time it work and the second time it fails

Exception with also the columns configured.

@Route(value="grid-serialization-issue", layout = GridSerializationIssueWithColumnLayout.class)
public class GridSerializationIssueWithColumn extends VerticalLayout implements HasUrlParameter<String>{
	private static final long serialVersionUID = 1L;
	private Grid<Country> grid;

	public GridSerializationIssueWithColumn() {
		setHeightFull();

		grid = new Grid<>();
		grid.setSizeFull();

		grid.addColumn(Country::getCountrycode).setHeader("ISO Code").setWidth("100px").setFlexGrow(0);
		grid.addColumn(Country::getCountry).setHeader("Name").setFlexGrow(1);

		grid.setSelectionMode(SelectionMode.NONE);

		add(grid);
	}

	public void onFilterChange(String filter) {
		Country france = new Country();
		france.setCountry("France");
		france.setCountrycode("FR");

		grid.setItems(List.of(france));
	}

	@Override
	public void setParameter(BeforeEvent event, @OptionalParameter String parameter) {
		onFilterChange(parameter);
	}

	public static class GridSerializationIssueWithColumnLayout extends AppLayout {
		private static final long serialVersionUID = 1L;

		public GridSerializationIssueWithColumnLayout() {
			Button test = new Button("Serialize", event -> {
				try {
					ByteArrayOutputStream bos = new ByteArrayOutputStream();
					try(ObjectOutputStream oos = new ObjectOutputStream(bos)){
						oos.writeObject(GridSerializationIssueWithColumnLayout.this);
						oos.flush();
					}
					try(ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray())){
						try(ObjectInputStream ois = new ObjectInputStream(bis)){
							GridSerializationIssueWithColumnLayout readObject = (GridSerializationIssueWithColumnLayout) ois.readObject();
						}
					}
					Notification.show("OK");
				}catch (Exception e) {
					e.printStackTrace();
					Notification.show("FAILED: " + e.getMessage());
				}
			});
			
			addToNavbar(test);
		}
	}

	public static class Country implements Serializable {
		private static final long serialVersionUID = 1L;

		private String country;

		private String countrycode;

		public Country(){}

		public String getCountry() {
			return country;
		}

		public void setCountry(String country) {
			this.country = country;
		}

		public String getCountrycode() {
			return countrycode;
		}

		public void setCountrycode(String countrycode) {
			this.countrycode = countrycode;
		}
	}
}

The example below succeed the first time but fails the subsequents tries

@Route(value="grid-serialization-issue2")
public class GridSerializationIssue extends VerticalLayout implements HasUrlParameter<String>{
	private static final long serialVersionUID = 1L;
	private Grid<Country> grid;

	public GridSerializationIssue() {
		setHeightFull();
		
		Button test = new Button("Serialize", event -> {
			try {
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				try(ObjectOutputStream oos = new ObjectOutputStream(bos)){
					oos.writeObject(UI.getCurrent());
					oos.flush();
				}
				try(ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray())){
					try(ObjectInputStream ois = new ObjectInputStream(bis)){
						UI readObject = (UI) ois.readObject();
					}
				}
				Notification.show("OK");
			}catch (Exception e) {
				e.printStackTrace();
				Notification.show("FAILED: " + e.getMessage());
			}
		});
		
		add(test);

		grid = new Grid<>();
		grid.setSizeFull();

		grid.addColumn(Country::getCountrycode).setHeader("ISO Code").setWidth("100px").setFlexGrow(0);
		grid.addColumn(Country::getCountry).setHeader("Name").setFlexGrow(1);

		grid.setSelectionMode(SelectionMode.NONE);

		add(grid);
	}

	public void onFilterChange(String filter) {
		Country france = new Country();
		france.setCountry("France");
		france.setCountrycode("FR");

		grid.setItems(List.of(france));
	}

	@Override
	public void setParameter(BeforeEvent event, @OptionalParameter String parameter) {
		onFilterChange(parameter);
	}

	public static class Country implements Serializable {
		private static final long serialVersionUID = 1L;

		private String country;

		private String countrycode;

		public Country(){}

		public String getCountry() {
			return country;
		}

		public void setCountry(String country) {
			this.country = country;
		}

		public String getCountrycode() {
			return countrycode;
		}

		public void setCountrycode(String countrycode) {
			this.countrycode = countrycode;
		}
	}
}

davidef avatar Sep 27 '22 11:09 davidef

Looks similar to this issue https://github.com/vaadin/flow/issues/6216.

mshabarov avatar Oct 04 '22 10:10 mshabarov

This ticket/PR has been released with Vaadin 23.1.13.

vaadin-bot avatar Oct 24 '22 10:10 vaadin-bot

@caalador We just retested the examples above with vaadin 23.2.5 which should include the fix (according to https://github.com/vaadin/flow/commits/23.2.5 ) but they are still failing. Also the issue was not present (for us) in vaadin 23.1.x

davidef avatar Oct 24 '22 10:10 davidef

Flow 23.2.5 will be in Vaadin 23.2.6 that should be available in mvn in a moment

caalador avatar Oct 24 '22 11:10 caalador

This ticket/PR has been released with Vaadin 23.2.6.

vaadin-bot avatar Oct 24 '22 12:10 vaadin-bot