AnsweredAssumed Answered

[Activiti Explorer] NPE in LazyLoadingContainer

Question asked by ncolomer on Jan 11, 2013
Latest reply on Jan 15, 2013 by frederikheremans1
Hi Activiti community,

We detected a NPE in Activiti Explorer, while trying to add a user in a group via the User Administration Panel.

Such try causes the following stacktrace :


Caused by: java.lang.NullPointerException
   at org.activiti.explorer.data.LazyLoadingContainer.getContainerProperty(LazyLoadingContainer.java:87)
   at com.vaadin.ui.AbstractSelect.getContainerProperty(AbstractSelect.java:745)
   at com.vaadin.ui.Table.refreshRenderedCells(Table.java:1550)
   at com.vaadin.ui.Table.addContainerProperty(Table.java:3027)
   at org.activiti.explorer.ui.management.identity.GroupSelectionPopupWindow.initGroupTable(GroupSelectionPopupWindow.java:82)
   at org.activiti.explorer.ui.management.identity.GroupSelectionPopupWindow.<init>(GroupSelectionPopupWindow.java:64)
   at org.activiti.explorer.ui.management.identity.UserDetailPanel$6.buttonClick(UserDetailPanel.java:363)
   …

You can reproduce the exception by deploying a fresh Activiti Explorer WAR and add some assignement and security-role groups.

After some investigation, the culprit is the GroupSelectionQuery. Indeed, the actual loadItems() implementation executes a paginated query and then filters result groups to retain only ones the user does not belong to. Thus it can cause a desynchronization of start and end indexes and throw NPE in the LazyLoadingContainer.getContainerProperty() method.

Here is how I temporarly fixed the problem :

    public List<Item> loadItems(int start, int count) {
        // Retrieve groups user belong to
        Set<String> currentGroups = getCurrentGroups();
        // Retrieve all groups and remove ones that user is already member of
        List<Group> groups = identityService.createGroupQuery().orderByGroupType().asc().orderByGroupId().asc().orderByGroupName().asc().list();
        List<Group> groupsFiltered = new ArrayList<Group>();
        for (Group group : groups) {
            if (!currentGroups.contains(group.getId()))
                groupsFiltered.add(group);
        }
        // Compute end index, extract sublist, create and populate result list
        int end = start + count <= groupsFiltered.size() ? start + count : groupsFiltered.size();
        List<Item> groupItems = new ArrayList<Item>();
        for (Group group : groupsFiltered.subList(start, end)) {
            groupItems.add(new GroupSelectionItem(group));
        }
        return groupItems;
    }

Please note this new implementation is no more lazy as it has to load all existing groups. I agree this could be more efficient but the GroupQuery builder currently does not allow search for groups that user does not belong to (we can only do the opposite, i.e. search for group the user belong to). I think add a new groupNotMember() method in the GroupQuery class could definitely fix the problem : the filtering would be done during the DB SELECT and return a consistent result set.

Nicolas

Outcomes