Vaadin 8 Grid RefreshItem doesnot work on update

Why is it so hard to refresh row in Grid with the updated row object. I am using basic CRUD example of Vaadin 8 on Eclipse with Grid.
However, on update of row, the changed value is not available in the grid. I need to do an explicit grid.setItem() again.

Is there anyone who is able to get this working for Grid. Appreciate if you could share some pointers.

Here is the snippet below

public class MsgDataService implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -746522368681183544L;

	private static MsgDataService INSTANCE;

	private List<ConsoleEntry> consoleEntryList;

	// private HashMap<Integer, ConsoleEntry> msgEntryMap;

	private Console console;

	private MsgDataService() {

	}

	public synchronized static MsgDataService getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new MsgDataService();
		}
		return INSTANCE;
	}

	public synchronized List<ConsoleEntry> getAllConsoleEntry() {
		consoleEntryList = console.getMessageEntryList();
		return consoleEntryList;
	}

	public synchronized HashMap<Integer, ConsoleEntry> getConsoleEntryMap() {
		HashMap<Integer, ConsoleEntry> msgEntryMap = console.getMsgEntryMap();
		return msgEntryMap;
	}

	public synchronized void updateConsoleEntry(ConsoleEntry p) {

		if (p != null) {
			HashMap<Integer, ConsoleEntry> consoleEntryMap = getConsoleEntryMap();

			ConsoleEntry consoleEntry = consoleEntryMap.get(p.getEntryId());
			if (consoleEntry != null) {
				consoleEntryMap.put(p.getEntryId(), p);

			}
		} else {
			throw new IllegalArgumentException("No console with id " + p.getEntryId() + " found");
		}

	}

	public synchronized ConsoleEntry getConsoleEntryById(int entryId) {
		ConsoleEntry consoleEntry = getConsoleEntryMap().get(entryId);
		if (consoleEntry != null) {
			return consoleEntry;
		}

		return null;
	}

	public synchronized void deleteConsoleEntry(int consoleId) {
		ConsoleEntry p = getConsoleEntryById(consoleId);
		if (p == null) {
			throw new IllegalArgumentException("Console with id " + consoleId + " not found");
		}
		consoleEntryList.remove(p);
	}

	public Console getConsole() {
		return console;
	}

	public void setConsole(Console console) {
		this.console = console;
	}
}

public class MsgDataProvider extends AbstractDataProvider<ConsoleEntry, String> {

	/**
	 * 
	 */
	private static final long serialVersionUID = -357116593449141449L;
	/** Text filter that can be changed separately. */
	private String filterText = "";

	/**
	 * Store given product to the backing data service.
	 * 
	 * @param consoleEntry
	 *            the updated or new consoleEntry
	 */
	public void save(ConsoleEntry consoleEntry) {
		boolean newProduct = consoleEntry.getEntryId() == -1;

		MsgDataService.getInstance().updateConsoleEntry(consoleEntry);
		if (newProduct) {
			refreshAll();
		} else {
			refreshItem(consoleEntry);
		}
	}

	/**
	 * Delete given product from the backing data service.
	 * 
	 * @param product
	 *            the product to be deleted
	 */
	public void delete(ConsoleEntry consoleEntry) {
		// MsgDataService.getInstance().deleteProduct(consoleEntry.getEntryId());
		refreshAll();
	}

	/**
	 * Sets the filter to use for the this data provider and refreshes data.
	 * <p>
	 * Filter is compared for product name, availability and category.
	 * 
	 * @param filterText
	 *            the text to filter by, never null
	 */
	public void setFilter(String filterText) {
		Objects.requireNonNull(filterText, "Filter text cannot be null");
		if (Objects.equals(this.filterText, filterText.trim())) {
			return;
		}
		this.filterText = filterText.trim();

		refreshAll();
	}

	@Override
	public Integer getId(ConsoleEntry consoleEntry) {
		Objects.requireNonNull(consoleEntry, "Cannot provide an id for a null consoleEntry.");

		return consoleEntry.getEntryId();
	}

	@Override
	public boolean isInMemory() {
		return true;
	}

	@Override
	public int size(Query<ConsoleEntry, String> t) {
		return (int) fetch(t).count();
	}

	private boolean passesFilter(Object object, String filterText) {
		return object != null && object.toString().toLowerCase(Locale.ENGLISH).contains(filterText);
	}

	@Override
	public Stream<ConsoleEntry> fetch(Query<ConsoleEntry, String> query) {
		return MsgDataService.getInstance().getAllConsoleEntry().stream();
	}

}

private void createMsgGrid(Console inputConsole) {

		msgGrid = new Grid();

		List<ConsoleEntry> messageEntryListSize = new ArrayList<>();
		if (inputConsole != null) {
			dataService = MsgDataService.getInstance();
			dataService.setConsole(inputConsole);
			msgGrid.setDataProvider(dataProvider);
		}
	}
	
	private void handleHideRowMenuItem(ConsoleEntry selectedConsoleItem) {
				selectedConsoleItem.hide();	
			MsgDataService.getInstance().updateConsoleEntry(selectedConsoleItem);
			dataProvider.refreshItem(selectedConsoleItem);
	}

As shown in the snippet, dataProvider.refreshItem() doesnot refresh the Grid with new values.

TIA

I would verify that you have checked this, what is mentioned in our JavaDocs

void refreshItem(T item)

Refreshes the given item. This method should be used to inform all DataProviderListeners that an item has been updated or replaced with a new instance.

For this to work properly, the item must either implement #equals(Object) and #hashCode() to consider both the old and the new item instances to be equal, or alternatively getId(Object) should be implemented to return an appropriate identifier.

Tatu Lund:
I would verify that you have checked this, what is mentioned in our JavaDocs

void refreshItem(T item)

Refreshes the given item. This method should be used to inform all DataProviderListeners that an item has been updated or replaced with a new instance.

For this to work properly, the item must either implement #equals(Object) and #hashCode() to consider both the old and the new item instances to be equal, or alternatively getId(Object) should be implemented to return an appropriate identifier.

Please find the below item code which contains equals, hashcode and getId.
However, it fails to update the UI. But works when I reset the dataProvider on grid, which is not expected.

@Override
public boolean equals(Object obj) {
	if (obj instanceof ConsoleEntry) {
		ConsoleEntry temp = (ConsoleEntry) obj;
		return (this.getId() == temp.getId());
	}
	return false;
}

@Override
public int hashCode() {
	return getId();
}

public int getId() {
	return getMsgRef().hashCode();
}

Appreciate if you could help with this issue.

Tatu Lund:
I would verify that you have checked this, what is mentioned in our JavaDocs

void refreshItem(T item)

Refreshes the given item. This method should be used to inform all DataProviderListeners that an item has been updated or replaced with a new instance.

For this to work properly, the item must either implement #equals(Object) and #hashCode() to consider both the old and the new item instances to be equal, or alternatively getId(Object) should be implemented to return an appropriate identifier.

I used the CRUD example of Vaadin 8 in Eclipse and Update doesnot refresh the Grid. Could you share an working example of Update Grid.

I tried it in one of my test apps, it works. Find bellow code that you can apply with simple Vaadin 8 project archetype. Clicking button updates the number of the first item.

public class SimplePojo implements Serializable {
    private long id;
    private String description;
    private boolean yes;
    private boolean truth;
    private Date date;
    private BigDecimal number;
    private Double stars;
    
    public SimplePojo() {
    }

    public SimplePojo(long id, String description, boolean yes, Date date, BigDecimal number, Double stars) {
        this.id = id;
        this.description = description;
        this.yes = yes;
        this.date = date;
        this.number = number;
    		this.stars =  (double) stars;
    }

	public Double getStars() {
		return stars;
	}
	
	public void setStars(Double stars) {
		this.stars = stars;
	}
	
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public BigDecimal getNumber() {
        return number;
    }

    public void setNumber(BigDecimal number) {
        this.number = number;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
    
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean isYes() {
        return yes;
    }

    public void setYes(boolean yes) {
        this.yes = yes;
    }

    public boolean isTruth() {
        return truth;
    }

    public void setTruth(boolean truth) {
        this.truth = truth;
    }

}

public class DemoUI extends UI {

  @Override
  protected void init(VaadinRequest request) {

    Grid<SimplePojo> grid1 = new Grid<>();

    Random random = new Random(4837291937l);
    List<SimplePojo> data = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
      data.add(new SimplePojo(i, "Bean", true, new Date(),
        BigDecimal.valueOf(random.nextDouble() * 100), Double
            .valueOf(random.nextInt(5))));
      }
      grid1.addColumn(SimplePojo::getDescription);
      grid1.addColumn(SimplePojo::getStars);
      grid1.addColumn(SimplePojo::isTruth);
      grid1.addColumn(SimplePojo::getDate);
      grid1.addColumn(SimplePojo::getNumber);
      grid1.setItems(data);
      grid1.setSizeFull();

    Button updateButton = new Button("Update", event -> {
      SimplePojo item = grid1.getDataCommunicator().fetchItemsWithRange(0, 1).get(0);
      item.setNumber(item.getNumber().add(BigDecimal.ONE));
      grid1.getDataProvider().refreshItem(item);
    });

    VerticalLayout vLayout= new VerticalLayout();
    vLayout.addComponent(grid1,updateButton);
    setContent(vLayout);
  }
}

Tatu Lund:
I tried it in one of my test apps, it works. Find bellow code that you can apply with simple Vaadin 8 project archetype. Clicking button updates the number of the first item.

public class SimplePojo implements Serializable {
    private long id;
    private String description;
    private boolean yes;
    private boolean truth;
    private Date date;
    private BigDecimal number;
    private Double stars;
    
    public SimplePojo() {
    }

    public SimplePojo(long id, String description, boolean yes, Date date, BigDecimal number, Double stars) {
        this.id = id;
        this.description = description;
        this.yes = yes;
        this.date = date;
        this.number = number;
    		this.stars =  (double) stars;
    }

	public Double getStars() {
		return stars;
	}
	
	public void setStars(Double stars) {
		this.stars = stars;
	}
	
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public BigDecimal getNumber() {
        return number;
    }

    public void setNumber(BigDecimal number) {
        this.number = number;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
    
    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean isYes() {
        return yes;
    }

    public void setYes(boolean yes) {
        this.yes = yes;
    }

    public boolean isTruth() {
        return truth;
    }

    public void setTruth(boolean truth) {
        this.truth = truth;
    }

}

public class DemoUI extends UI {

  @Override
  protected void init(VaadinRequest request) {

    Grid<SimplePojo> grid1 = new Grid<>();

    Random random = new Random(4837291937l);
    List<SimplePojo> data = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
      data.add(new SimplePojo(i, "Bean", true, new Date(),
        BigDecimal.valueOf(random.nextDouble() * 100), Double
            .valueOf(random.nextInt(5))));
      }
      grid1.addColumn(SimplePojo::getDescription);
      grid1.addColumn(SimplePojo::getStars);
      grid1.addColumn(SimplePojo::isTruth);
      grid1.addColumn(SimplePojo::getDate);
      grid1.addColumn(SimplePojo::getNumber);
      grid1.setItems(data);
      grid1.setSizeFull();

    Button updateButton = new Button("Update", event -> {
      SimplePojo item = grid1.getDataCommunicator().fetchItemsWithRange(0, 1).get(0);
      item.setNumber(item.getNumber().add(BigDecimal.ONE));
      grid1.getDataProvider().refreshItem(item);
    });

    VerticalLayout vLayout= new VerticalLayout();
    vLayout.addComponent(grid1,updateButton);
    setContent(vLayout);
  }
}

Thanks for the working example. I was able to finally get it to work. Also, I dont need customDataProvider and customDataService.

Vaadin 8 no longer has Container. The Vaadin data model has been largely simplified. Basically, there is no data model anymore. There are just data providers for the various elements. What you want to look into is DataProvider, as it largely replaces Container