Flow: DataProvider IndexOutOfBoundsException when providing offset/limit

Created on 7 Oct 2019  路  2Comments  路  Source: vaadin/flow

Description of the bug

The _Minimal reproducible example_ (see below) errors out (with IndexOutOfBoundsException) for me once I scroll past the first 49 results.

Expected behavior

No IndexOutOfBoundsException when using a range/offsets.

Actual behavior

IndexOutOfBoundsException as follows:

[INFO] java.lang.IndexOutOfBoundsException: Index 50 out of bounds for length 50
[INFO]  at jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) ~[?:?]
[INFO]  at jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70) ~[?:?]
[INFO]  at jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248) ~[?:?]
[INFO]  at java.util.Objects.checkIndex(Objects.java:372) ~[?:?]
[INFO]  at java.util.ArrayList.get(ArrayList.java:458) ~[?:?]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.lambda$getJsonItems$3(DataCommunicator.java:611) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at java.util.stream.IntPipeline$1$1.accept(IntPipeline.java:180) ~[?:?]
[INFO]  at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:104) ~[?:?]
[INFO]  at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:699) ~[?:?]
[INFO]  at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[?:?]
[INFO]  at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[?:?]
[INFO]  at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[?:?]
[INFO]  at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?]
[INFO]  at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[?:?]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.getJsonItems(DataCommunicator.java:613) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.lambda$collectChangesToSend$2(DataCommunicator.java:571) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.applyIfNotEmpty(DataCommunicator.java:627) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.withMissing(DataCommunicator.java:621) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.collectChangesToSend(DataCommunicator.java:570) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.flush(DataCommunicator.java:473) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.data.provider.DataCommunicator.lambda$requestFlush$2f364bb9$1(DataCommunicator.java:421) ~[flow-data-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.internal.StateTree.lambda$runExecutionsBeforeClientResponse$1(StateTree.java:364) ~[flow-server-2.0.14.jar:2.0.14]
[INFO]  at java.util.ArrayList.forEach(ArrayList.java:1540) ~[?:?]
[INFO]  at com.vaadin.flow.internal.StateTree.runExecutionsBeforeClientResponse(StateTree.java:361) ~[flow-server-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.server.communication.UidlWriter.encodeChanges(UidlWriter.java:392) ~[flow-server-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.server.communication.UidlWriter.createUidl(UidlWriter.java:182) ~[flow-server-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.server.communication.UidlRequestHandler.writeUidl(UidlRequestHandler.java:116) ~[flow-server-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:89) ~[flow-server-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-2.0.14.jar:2.0.14]
[INFO]  at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1540) ~[flow-server-2.0.14.jar:2.0.14]

Minimal reproducible example

@Route
public class Test extends Div {
    public Test() {
        List<String> items = IntStream.range(0, 500).mapToObj(Integer::toString)
                .collect(Collectors.toList());

        DataProvider<String, Void> licenseDataProvider = DataProvider
                .fromCallbacks(query -> {
                    Stream<String> licenseStream = items
                            .subList(query.getOffset(), query.getLimit())
                            .stream();
                    return licenseStream;
                }, query -> items.size());

        Grid<String> grid = new Grid<>();
        grid.setDataProvider(licenseDataProvider);
        grid.addColumn(item -> item);
        add(grid);
    }
}

Versions

  • Vaadin: 14.0.7
  • Flow: 2.0.14
  • Grid Flow: 4.0.8
  • Java: dcevm-11.0.1+8
  • OS: macOS 10.15
  • Browser: Chromium 77.0.3865.90

Related issues

Most helpful comment

You don't want to use query.getLimit() as the second parameter to subList.
Simply use items.stream().skip(query.getOffset()).limit(query.getLimit()); or use something like query.getOffset() + query.getLimit() as a second parameter.

All 2 comments

You don't want to use query.getLimit() as the second parameter to subList.
Simply use items.stream().skip(query.getOffset()).limit(query.getLimit()); or use something like query.getOffset() + query.getLimit() as a second parameter.

Thank you very much for precise issue description which exact code snippet!
:+1:

But in fact the ticket is invalid.

The previous comment is absolutely right :https://github.com/vaadin/flow/issues/6631#issuecomment-539661604.
List.subList() uses the first argument as offset ( the very fist index) and the second argument as the last index in the list.
You are passing query.getLimit() which is wrong. It should be query.getOffset() + query.getLimit() as precisely said in the comment .
Thank you @INPUTsys-Chris for the exact answer :+1:

Was this page helpful?
0 / 5 - 0 ratings