Dear Team,
I am trying to implement a lazy loading grid based on the masterdetail example from start.vaadin.com (Version 14.0.4).
Therefore we added a postgres Database as the datasource and added a dataprovider taken from the documentation.
We added 100000 datasets. When we scroll through the data slowly, the grid works fine but when we scroll fast we get an IndexOutOfBoundsException.
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657) ~[na:1.8.0_191]
at java.util.ArrayList.get(ArrayList.java:433) ~[na:1.8.0_191]
at com.vaadin.flow.data.provider.DataCommunicator.lambda$getJsonItems$3(DataCommunicator.java:611) ~[flow-data-2.0.11.jar:2.0.11]
at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250) ~[na:1.8.0_191]
at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110) ~[na:1.8.0_191]
at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693) ~[na:1.8.0_191]
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_191]
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_191]
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_191]
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_191]
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) ~[na:1.8.0_191]
at com.vaadin.flow.data.provider.DataCommunicator.getJsonItems(DataCommunicator.java:613) ~[flow-data-2.0.11.jar:2.0.11]
at com.vaadin.flow.data.provider.DataCommunicator.collectChangesToSend(DataCommunicator.java:556) ~[flow-data-2.0.11.jar:2.0.11]
at com.vaadin.flow.data.provider.DataCommunicator.flush(DataCommunicator.java:473) ~[flow-data-2.0.11.jar:2.0.11]
at com.vaadin.flow.data.provider.DataCommunicator.lambda$requestFlush$2f364bb9$1(DataCommunicator.java:421) ~[flow-data-2.0.11.jar:2.0.11]
at com.vaadin.flow.internal.StateTree.lambda$runExecutionsBeforeClientResponse$1(StateTree.java:364) ~[flow-server-2.0.11.jar:2.0.11]
at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_191]
at com.vaadin.flow.internal.StateTree.runExecutionsBeforeClientResponse(StateTree.java:361) ~[flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.server.communication.UidlWriter.encodeChanges(UidlWriter.java:392) ~[flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.server.communication.UidlWriter.createUidl(UidlWriter.java:182) ~[flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.server.communication.UidlRequestHandler.writeUidl(UidlRequestHandler.java:116) ~[flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:89) ~[flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1540) ~[flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:246) [flow-server-2.0.11.jar:2.0.11]
at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:95) [vaadin-spring-12.0.5.jar:na]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750) [javax.servlet-api-4.0.1.jar:4.0.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:352) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.springframework.web.servlet.mvc.ServletForwardingController.handleRequestInternal(ServletForwardingController.java:141) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:177) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:52) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:665) [javax.servlet-api-4.0.1.jar:4.0.1]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) [spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750) [javax.servlet-api-4.0.1.jar:4.0.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-embed-websocket-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:41002) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) [tomcat-embed-core-9.0.22.jar:9.0.22]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.22.jar:9.0.22]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_191]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.22.jar:9.0.22]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]
This is the java code:
package com.example.application.spring.views.dashboard;
import com.example.application.spring.BroadcastMessage;
import com.example.application.spring.Broadcaster;
import com.example.application.spring.MainView;
import com.example.application.spring.backend.BackendService;
import com.example.application.spring.backend.Employee;
import com.example.application.spring.views.dashboard.DashboardView.DashboardViewModel;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.DetachEvent;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.gridpro.GridPro;
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.provider.CallbackDataProvider;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.router.AfterNavigationEvent;
import com.vaadin.flow.router.AfterNavigationObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouteAlias;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.templatemodel.TemplateModel;
import java.io.Serializable;
import java.util.List;
@Route(value = "dashboard", layout = MainView.class)
@RouteAlias(value = "", layout = MainView.class)
@PageTitle("Dashboard")
@JsModule("./src/views/dashboard/dashboard-view.js")
@Tag("dashboard-view")
public class DashboardView extends PolymerTemplate<DashboardViewModel> implements AfterNavigationObserver, Serializable {
public static interface DashboardViewModel extends TemplateModel {
}
private final BackendService service;
@Id
private GridPro<Employee> employees;
@Id
private TextField firstname;
@Id
private TextField lastname;
@Id
private TextField email;
@Id
private PasswordField password;
@Id
private Button cancel;
@Id
private Button save;
private Binder<Employee> binder;
private Employee selected;
Registration broadcasterRegistration;
public DashboardView(BackendService service) {
this.service = service;
configureGrid(service);
// Configure Form
binder = new Binder<>(Employee.class);
// Bind fields. This where you'd define e.g. validation rules
binder.bindInstanceFields(this);
// note that password field isn't bound since that property doesn't exist in
// Employee
// the grid valueChangeEvent will clear the form too
cancel.addClickListener(e -> {
employees.asSingleSelect().clear();
selected = null;
});
save.addClickListener(e -> {
binder.writeBeanIfValid(selected);
service.save(selected);
CallbackDataProvider<Employee, Void> employeeList = (CallbackDataProvider<Employee, Void>) employees.getDataProvider();
employeeList.refreshItem(selected);
Broadcaster.broadcast(new BroadcastMessage(selected, BroadcastMessage.BroadcastMessageType.REFRESH_DASHBOARD));
});
}
private void configureGrid(BackendService service) {
// Configure Grid
employees.setDataProvider(DataProvider.fromCallbacks(
// First callback fetches items based on a query
query -> {
// The index of the first item to load
int offset = query.getOffset();
// The number of items to load
int limit = query.getLimit();
List<Employee> persons = service.getEmployees(offset, limit);
return persons.stream();
},
// Second callback fetches the number of items
// for a query
query -> service.getEmployeeCount()));
employees.addColumn(Employee::getFirstname).setHeader("First name");
employees.addColumn(Employee::getLastname).setHeader("Last name");
employees.addColumn(Employee::getEmail).setHeader("Email");
employees.setSelectionMode(Grid.SelectionMode.SINGLE);
//when a row is selected or deselected, populate form
employees.asSingleSelect().addValueChangeListener(event -> populateForm(event.getValue()));
}
@Override
public void afterNavigation(AfterNavigationEvent event) {
}
private void populateForm(Employee value) {
this.selected = value;
// Value can be null as well, that clears the form
binder.readBean(value);
// The password field isn't bound through the binder, so handle that
password.setValue("");
}
@Override
protected void onAttach(AttachEvent attachEvent) {
UI ui = attachEvent.getUI();
broadcasterRegistration = Broadcaster.register(broadcastMessage -> {
if (BroadcastMessage.BroadcastMessageType.REFRESH_DASHBOARD.equals(broadcastMessage.getMessageType())) {
ui.access(() -> employees.getDataProvider().refreshItem(broadcastMessage.employee));
}
});
}
@Override
protected void onDetach(DetachEvent detachEvent) {
broadcasterRegistration.remove();
}
}
The dataservice is a Spring Service Class with two Methods:
public List<Employee> getEmployees(int offset, int limit) {
Pageable pageable = PageRequest.of(offset, limit);
return employeeRepository.findAllBy(pageable);
}
public int getEmployeeCount() {
int employees = Math.toIntExact(employeeRepository.count());
return employees;
}
Can you please advise how to solve the issue?
Kind regards
Florian