Grid drag/drop NPE - Cannot provide an id for a null item.

Hi,

I am experiencing a random NPE when using drag and drop with a grid. The grid is shown in a dialog to allow users to reorder a basic list of data.

[ERROR]
 2020-08-24 12:10:52 - Uncaught UI exception
java.lang.NullPointerException: Cannot provide an id for a null item.
	at java.util.Objects.requireNonNull(Objects.java:247) ~[?:?]

	at com.vaadin.flow.data.provider.DataProvider.getId(DataProvider.java:150) ~[flow-data-2.3.3.jar:2.3.3]

	at com.vaadin.flow.data.provider.KeyMapper.has(KeyMapper.java:105) ~[flow-data-2.3.3.jar:2.3.3]

	at com.vaadin.flow.data.provider.DataCommunicator.lambda$activate$5(DataCommunicator.java:643) ~[flow-data-2.3.3.jar:2.3.3]

	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[?:?]

	at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:442) ~[?:?]

The list of data in this grid only has 3 items but the Range object in the Vaadin code appears to report a length of 4.
It only takes 2 or 3 operations for the error to occur.
This is also used in a second dialog with a larger list, and I haven’t been able to replicate the issue there.

The grid drag/drop code is below:

public class ReorderableGridLayout<E> extends Grid<E>
{
	private E draggedItem;
	private final ListDataProvider<E> listDataProvider;
	private final List<E> listOfItems;

	public ReorderableGridLayout()
	{
		super();
		listOfItems = new ArrayList<>();
		listDataProvider = new ListDataProvider<>(listOfItems);
		this.setDataProvider(listDataProvider);
		this.setRowsDraggable(true);
		this.addDragStartListener(event -> {
			draggedItem = event.getDraggedItems().get(0);
			this.setDropMode(GridDropMode.BETWEEN);
		});

		this.addDragEndListener(event -> {
			draggedItem = null;
			this.setDropMode(null);
		});

		this.addDropListener(event -> {
			if(!event.getDropTargetItem().isPresent())
				return;
			E dropOverItem = event.getDropTargetItem().get();
			if (!dropOverItem.equals(draggedItem)) {
				listOfItems.remove(draggedItem);
				int dropIndex = listOfItems.indexOf(dropOverItem)
						+ (event.getDropLocation() == GridDropLocation.BELOW ? 1
						: 0);
				listOfItems.add(dropIndex, draggedItem);
				this.getDataProvider().refreshAll();
			}
		});
	}

	public void addItems(List<E> list)
	{
		listOfItems.clear();
		listOfItems.addAll(list);
		refreshAll();
	}

	public void refreshAll()
	{
		listDataProvider.refreshAll();
	}

	public List<E> getItems()
	{
		return listOfItems;
	}
}

Vaadin 14.3.3 and SpringBoot 2.3.3.

Thanks,
Dave

Hey Dave,

I compared it to my implementation. I have a null check on the dragged item:

if (!dropTarget.equals(draggedItem) && draggedItem != null)

It has been some time since I implemented it, but I vaguely remember having a similar issue, which was solved by this check.

Regards,
Eric

Edit: Just saw that you check your Optional. That post may not be that helpful ^^

Edit2: Is “this.getDataProvider()” returning the correct DataProvider?