Flow: DataProvider IndexOutOfBoundsException when providing offset/limit

Created on 5 Apr 2018  路  7Comments  路  Source: vaadin/flow

When implementig a Grid with a DataProvider using fromCallbacks method like:

    this.dataProvider = DataProvider.fromCallbacks(query -> {
            List<Sort.Order> sortOrders = new ArrayList<>();
            return crudService.findAll(query.getOffset(), query.getLimit(), sortOrders).stream();
        }, query -> {
            return crudService.count();
        });

I receive a java.lang.IndexOutOfBoundsException: Index: 50, Size: 50 after scroll inside grid for fetch more data.
Follow the stacktrace:

java.lang.IndexOutOfBoundsException: Index: 50, Size: 50
    at java.util.ArrayList.rangeCheck(ArrayList.java:653) ~[na:1.8.0_92]
    at java.util.ArrayList.get(ArrayList.java:429) ~[na:1.8.0_92]
    at com.vaadin.flow.data.provider.DataCommunicator.lambda$getJsonItems$3(DataCommunicator.java:529) ~[flow-data-1.0.0.beta5.jar:na]
    at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250) ~[na:1.8.0_92]
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110) ~[na:1.8.0_92]
    at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693) ~[na:1.8.0_92]
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_92]
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_92]
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_92]
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_92]
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) ~[na:1.8.0_92]
    at com.vaadin.flow.data.provider.DataCommunicator.getJsonItems(DataCommunicator.java:531) ~[flow-data-1.0.0.beta5.jar:na]
    at com.vaadin.flow.data.provider.DataCommunicator.lambda$collectChangesToSend$2(DataCommunicator.java:494) ~[flow-data-1.0.0.beta5.jar:na]
    at com.vaadin.flow.data.provider.DataCommunicator.applyIfNotEmpty(DataCommunicator.java:545) ~[flow-data-1.0.0.beta5.jar:na]
    at com.vaadin.flow.data.provider.DataCommunicator.withMissing(DataCommunicator.java:539) ~[flow-data-1.0.0.beta5.jar:na]
    at com.vaadin.flow.data.provider.DataCommunicator.collectChangesToSend(DataCommunicator.java:493) ~[flow-data-1.0.0.beta5.jar:na]
    at com.vaadin.flow.data.provider.DataCommunicator.flush(DataCommunicator.java:398) ~[flow-data-1.0.0.beta5.jar:na]
    at com.vaadin.flow.data.provider.DataCommunicator.lambda$requestFlush$2f364bb9$1(DataCommunicator.java:355) ~[flow-data-1.0.0.beta5.jar:na]
    at com.vaadin.flow.internal.StateTree.lambda$runExecutionsBeforeClientResponse$1(StateTree.java:335) ~[flow-server-1.0.0.beta5.jar:na]
    at java.util.ArrayList.forEach(ArrayList.java:1249) ~[na:1.8.0_92]
    at com.vaadin.flow.internal.StateTree.runExecutionsBeforeClientResponse(StateTree.java:332) ~[flow-server-1.0.0.beta5.jar:na]
    at com.vaadin.flow.server.communication.UidlWriter.encodeChanges(UidlWriter.java:288) ~[flow-server-1.0.0.beta5.jar:na]
    at com.vaadin.flow.server.communication.UidlWriter.createUidl(UidlWriter.java:133) ~[flow-server-1.0.0.beta5.jar:na]
    at com.vaadin.flow.server.communication.UidlRequestHandler.writeUidl(UidlRequestHandler.java:119) ~[flow-server-1.0.0.beta5.jar:na]
    at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:91) ~[flow-server-1.0.0.beta5.jar:na]
    at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-1.0.0.beta5.jar:na]
    at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1541) ~[flow-server-1.0.0.beta5.jar:na]
    at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:345) [flow-server-1.0.0.beta5.jar:na]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.28.jar:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.28.jar:8.5.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.28.jar:8.5.28]
BFP bug data provider

Most helpful comment

It still fails with java.lang.IndexOutOfBoundsException
if backend is not able to return the full "limit" of rows...
Vaadin 8 just calls the count method for an updated count

All 7 comments

Got the same issue. Waiting for fix...

Same here

This seems to be a implementation issue part by me. In the example above I'm using spring pageable to feed the DataProvider. Spring pageable uses pages not offset/limit. I modified the findAll/count to use offset ? limit ? and count(*) and now it's working.

@dg0s We have the same Issue, but I am not sure what you mean by

I modified the findAll/count to use offset ? limit ? and count(*)

could you post the snippet that works for you?

I'm using native query to implement the offset/limit. However you can use also JPQL/Criteriabuilder.

Respository.java

@Query(value = "SELECT m.id as key, m.codigo as code, m.nome as description FROM medico m WHERE m.codigo IS NOT NULL AND NOT m.obsoleto AND UPPER(m.codigo) LIKE UPPER(?1) OFFSET(?2) LIMIT(?3)",nativeQuery = true)
List<SearchableResultDto> findAll(String value, int start, int limit);

@Query(value = "SELECT COUNT(*) FROM medico m WHERE m.codigo IS NOT NULL AND NOT m.obsoleto AND UPPER(m.codigo) LIKE UPPER(?1)",nativeQuery = true)
int count(String value);

It still fails with java.lang.IndexOutOfBoundsException
if backend is not able to return the full "limit" of rows...
Vaadin 8 just calls the count method for an updated count

I use this OffsetBasedPageRequest from stackoverflow
usage:

    Pageable pageable = new OffsetBasedPageRequest(offset, limit);
    return this.dataServices.findAllInclusive(pageable);

and the source code:

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.data.domain.AbstractPageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.io.Serializable;

/**
* Created by Ergin
**/
public class OffsetBasedPageRequest implements Pageable, Serializable {

    private static final long serialVersionUID = -25822477129613575L;

    private int limit;
    private int offset;
    private final Sort sort;

    /**
     * Creates a new {@link OffsetBasedPageRequest} with sort parameters applied.
     *
     * @param offset zero-based offset.
     * @param limit  the size of the elements to be returned.
     * @param sort   can be {@literal null}.
     */
    public OffsetBasedPageRequest(int offset, int limit, Sort sort) {
        if (offset < 0) {
            throw new IllegalArgumentException("Offset index must not be less than zero!");
        }

        if (limit < 1) {
            throw new IllegalArgumentException("Limit must not be less than one!");
        }
        this.limit = limit;
        this.offset = offset;
        this.sort = sort;
    }

    /**
     * Creates a new {@link OffsetBasedPageRequest} with sort parameters applied.
     *
     * @param offset     zero-based offset.
     * @param limit      the size of the elements to be returned.
     * @param direction  the direction of the {@link Sort} to be specified, can be {@literal null}.
     * @param properties the properties to sort by, must not be {@literal null} or empty.
     */
    public OffsetBasedPageRequest(int offset, int limit, Sort.Direction direction, String... properties) {
        this(offset, limit, new Sort(direction, properties));
    }

    /**
     * Creates a new {@link OffsetBasedPageRequest} with sort parameters applied.
     *
     * @param offset zero-based offset.
     * @param limit  the size of the elements to be returned.
     */
    public OffsetBasedPageRequest(int offset, int limit) {
        this(offset, limit, new Sort(Sort.Direction.ASC,"id"));
    }

    @Override
    public int getPageNumber() {
        return offset / limit;
    }

    @Override
    public int getPageSize() {
        return limit;
    }

    @Override
    public int getOffset() {
        return offset;
    }

    @Override
    public Sort getSort() {
        return sort;
    }

    @Override
    public Pageable next() {
        return new OffsetBasedPageRequest(getOffset() + getPageSize(), getPageSize(), getSort());
    }

    public OffsetBasedPageRequest previous() {
        return hasPrevious() ? new OffsetBasedPageRequest(getOffset() - getPageSize(), getPageSize(), getSort()) : this;
    }


    @Override
    public Pageable previousOrFirst() {
        return hasPrevious() ? previous() : first();
    }

    @Override
    public Pageable first() {
        return new OffsetBasedPageRequest(0, getPageSize(), getSort());
    }

    @Override
    public boolean hasPrevious() {
        return offset > limit;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;

        if (!(o instanceof OffsetBasedPageRequest)) return false;

        OffsetBasedPageRequest that = (OffsetBasedPageRequest) o;

        return new EqualsBuilder()
                .append(limit, that.limit)
                .append(offset, that.offset)
                .append(sort, that.sort)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(limit)
                .append(offset)
                .append(sort)
                .toHashCode();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("limit", limit)
                .append("offset", offset)
                .append("sort", sort)
                .toString();
    }
}
Was this page helpful?
0 / 5 - 0 ratings