Stack Overflow Error on filtering CollectionPropertyContainer

Hello,

I’ve got a composition table that I need to filter values out of and then sort by some dates. I was able to get the sort working, but then tried filtering where another date value is null. I’ve followed an example from the Property Containers page in the manual. When I do this, I receive a StackOverflowError.

The code in the controller file is this:

    @Subscribe(id = "workCompletionDc", target = Target.DATA_CONTAINER)
    public void onWorkCompletionDcCollectionChange(CollectionContainer.CollectionChangeEvent<WorkCompletion> event) {
        filterCompleted();
        sortWorkCompletion();
    }

    private void filterCompleted() {
        List<WorkCompletion> filtered = getEditedEntity().getWorkCompletion().stream()
                .filter(workCompletion -> workCompletion.getCompletedDate() == null)
                .collect(Collectors.toList());
        workCompletionDc.setDisconnectedItems(filtered);
    }

    private void sortWorkCompletion() {
        List<WorkCompletion> workCompletionList = workCompletionDc.getMutableItems();
        workCompletionList.sort((o1, o2) -> {
            Date date1 = o1.getExternalDueDate();
            Date date2 = o2.getExternalDueDate();

            return Objects.compare(date1, date2, Comparator.nullsLast(Comparator.naturalOrder()));
        });
    }

The stack trace is showing the following:

java.lang.StackOverflowError
	at java.base/java.lang.String.indexOf(String.java:1578)
	at java.base/java.lang.String.indexOf(String.java:1535)
	at java.base/java.lang.String.split(String.java:2274)
	at java.base/java.lang.String.split(String.java:2364)
	at com.haulmont.cuba.security.role.PermissionsUtils.getWildcardPermissionValue(PermissionsUtils.java:121)
	at com.haulmont.cuba.security.role.PermissionsUtils.getResultingPermissionValue(PermissionsUtils.java:107)
	at com.haulmont.cuba.security.global.UserSession.isPermitted(UserSession.java:334)
	at com.haulmont.cuba.security.global.UserSession.isPermitted(UserSession.java:316)
	at com.haulmont.cuba.security.global.UserSession.isEntityOpPermitted(UserSession.java:281)
	at com.haulmont.cuba.core.sys.SecurityImpl.isEntityOpPermitted(SecurityImpl.java:76)
	at com.haulmont.cuba.gui.actions.list.CreateAction.isPermitted(CreateAction.java:275)
	at com.haulmont.cuba.gui.components.actions.BaseAction.refreshState(BaseAction.java:150)
	at com.haulmont.cuba.web.gui.components.WebAbstractTable.refreshActionsState(WebAbstractTable.java:1224)
	at com.haulmont.cuba.web.gui.components.WebAbstractTable.tableSourceItemSetChanged(WebAbstractTable.java:1639)
	at com.haulmont.cuba.web.gui.components.table.TableDataContainer.datasourceItemSetChanged(TableDataContainer.java:283)
	at com.haulmont.bali.events.EventHub.publish(EventHub.java:170)
	at com.haulmont.cuba.gui.components.data.table.ContainerTableItems.containerCollectionChanged(ContainerTableItems.java:93)
	at com.haulmont.bali.events.EventHub.publish(EventHub.java:170)
	at com.haulmont.cuba.gui.model.impl.CollectionContainerImpl.fireCollectionChanged(CollectionContainerImpl.java:202)
	at com.haulmont.cuba.gui.model.impl.CollectionContainerImpl.setItems(CollectionContainerImpl.java:98)
	at com.haulmont.cuba.gui.model.impl.CollectionPropertyContainerImpl.setDisconnectedItems(CollectionPropertyContainerImpl.java:58)
	at com.company.hermes.web.screens.work.WorkEdit.filterCompleted(WorkEdit.java:221)
	at com.company.hermes.web.screens.work.WorkEdit.onWorkCompletionDcCollectionChange(WorkEdit.java:213)
	at com.haulmont.bali.events.EventHub.publish(EventHub.java:170)
	at com.haulmont.cuba.gui.model.impl.CollectionContainerImpl.fireCollectionChanged(CollectionContainerImpl.java:202)
	at com.haulmont.cuba.gui.model.impl.CollectionContainerImpl.setItems(CollectionContainerImpl.java:98)
	at com.haulmont.cuba.gui.model.impl.CollectionPropertyContainerImpl.setDisconnectedItems(CollectionPropertyContainerImpl.java:58)
	at com.company.hermes.web.screens.work.WorkEdit.filterCompleted(WorkEdit.java:221)
	at com.company.hermes.web.screens.work.WorkEdit.onWorkCompletionDcCollectionChange(WorkEdit.java:213)
	at com.haulmont.bali.events.EventHub.publish(EventHub.java:170)
	at com.haulmont.cuba.gui.model.impl.CollectionContainerImpl.fireCollectionChanged(CollectionContainerImpl.java:202)
	at com.haulmont.cuba.gui.model.impl.CollectionContainerImpl.setItems(CollectionContainerImpl.java:98)
	at com.haulmont.cuba.gui.model.impl.CollectionPropertyContainerImpl.setDisconnectedItems(CollectionPropertyContainerImpl.java:58)
	at com.company.hermes.web.screens.work.WorkEdit.filterCompleted(WorkEdit.java:221)
	at com.company.hermes.web.screens.work.WorkEdit.onWorkCompletionDcCollectionChange(WorkEdit.java:213)
	at com.haulmont.bali.events.EventHub.publish(EventHub.java:170)

It seems to be stuck in a loop of some sort.
Let me know if there’s anything else I can provide.

Thanks in advance,
Adam

Hello,

based on your stacktrace:

  1. The onWorkCompletionDcCollectionChange() invokes filtering
  2. The filtering calls setDisconnectedItems() method
  3. Then setting items fires CollectionChangeEvent and so on.

Did you try to move your filtering from onWorkCompletionDcCollectionChange()? Also, you can try to unsubscribe from CollectionChangeEvent when you set items and then subscribe again.

Well, you’re definitely correct that the loop is originating from the collectionChange event which triggers the filter, which triggers the collectionChange event again, and so on.

I do need it to filter when the values are changed though. I’m curious as to exactly how I can unsubscribe from the collectionChange event and then resubscribe afterward. I searched the manual and didn’t get any hits for ‘unsubscribe’.

Any help with syntax of how to do this would be greatly appreciated.

Thanks!
Adam

In InitEvent subscribe to collection change event of your container:

private Subscription containerSubscription; // saveed subscription

@Subscribe
public void onInit(InitEvent event) {
    containerSubscription = container.addCollectionChangeListener(this::onContainerChange);
}

And when you set items remove subscription and subscribe again:

private void filterCompleted() {
    ...

    // remove subscription to avoid StackOverflow
    if (containerSubscription != null) {
        containerSubscription.remove();
        containerSubscription = null;
    }

    container.setDisconnectedItems(filteredItems);

    // sbuscribe again
    containerSubscription = container.addCollectionChangeListener(this::onContainerChange);
}

private void onContainerChange(CollectionContainer.CollectionChangeEvent<OrderLine> event) {
    filterCompleted();
}

Beautiful! Thank you very much for your detailed solution!