NullPointerException when calling TaskService#getIdentityLinksForTask in TaskListener on complete

XMLWordPrintable

      Forum post: https://forum.camunda.org/t/nullpointerexception-while-completing-a-task/5454

      Hi,

      we are trying to switch to camunda 7.8 but we are facing a NPE while trying to complete a UserTask via REST-API. The reason why this happens is rather curious:

      We registered a taskNotificationListener which sends notifications via websockets when a task gets completed (via taskService.getIdentityLinksForTask(delegateTask.getId())). This method uses the GetIdentityLinksForTaskCmd which programmatically adds a new IdentityLinkObject to the IdentityList. Thankfully the comment in this method already explains the NPE. Camunda then tries to complete and delete this task with all associated Identitylinks (TaskManager:L84). Since everything happens in the same thread, the TaskEntity is already cached in dbEntityCache AND the (dirty) List with IdentityLinkEntities is already loaded. An IdentityLinkEntity with ID [NULL] gets passed to the DbOperationsManager and the underlying DbEntityOperationComparator where it goes Boom. I’ve added the relevant code and the stacktrace below…

      the execute method in GetIdentityLinksForTaskCmd loads the IdentityLinks and adds a new Entity with ID [NULL] to the List.

          List<IdentityLink> identityLinks = (List) task.getIdentityLinks();
      
          // assignee is not part of identity links in the db.
          // so if there is one, we add it here.
          // @Tom: we discussed this long on skype and you agreed ;-)
          // an assignee *is* an identityLink, and so must it be reflected in the API
          //
          // Note: we cant move this code to the TaskEntity (which would be cleaner),
          // since the task.delete cascased to all associated identityLinks
          // and of course this leads to exception while trying to delete a non-existing identityLink
          if (task.getAssignee() != null) {
            IdentityLinkEntity identityLink = new IdentityLinkEntity();
            identityLink.setUserId(task.getAssignee());
            identityLink.setTask(task);
            identityLink.setType(IdentityLinkType.ASSIGNEE);
            identityLinks.add(identityLink);
          }
          if (task.getOwner() != null) {
            IdentityLinkEntity identityLink = new IdentityLinkEntity();
            identityLink.setUserId(task.getOwner());
            identityLink.setTask(task);
            identityLink.setType(IdentityLinkType.OWNER);
            identityLinks.add(identityLink);
          }
      
          return (List) task.getIdentityLinks();
      

      TaskEntity.getIdentityLinks() does not reload its IdentityLinks once initalized, so the dirty IdentityLinkEntity stays in the List

      public List<IdentityLinkEntity> getIdentityLinks() {
          if (!isIdentityLinksInitialized) {
            taskIdentityLinkEntities = Context
              .getCommandContext()
              .getIdentityLinkManager()
              .findIdentityLinksByTaskId(id);
            isIdentityLinksInitialized = true;
          }
      
          return taskIdentityLinkEntities;
        }
      

      NPE-Stacktrace

      Caused by: java.lang.NullPointerException: null
      	at java.lang.String.compareTo(String.java:1155) ~[na:1.8.0_74]
      	at org.camunda.bpm.engine.impl.db.entitymanager.operation.comparator.DbEntityOperationComparator.compare(DbEntityOperationComparator.java:35) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.db.entitymanager.operation.comparator.DbEntityOperationComparator.compare(DbEntityOperationComparator.java:24) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at java.util.TreeMap.put(TreeMap.java:552) ~[na:1.8.0_74]
      	at java.util.TreeSet.add(TreeSet.java:255) ~[na:1.8.0_74]
      	at org.camunda.bpm.engine.impl.db.entitymanager.operation.DbOperationManager.addOperation(DbOperationManager.java:75) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.performEntityOperation(DbEntityManager.java:572) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.flushCachedEntity(DbEntityManager.java:451) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.flushEntityCache(DbEntityManager.java:417) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.db.entitymanager.DbEntityManager.flush(DbEntityManager.java:283) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.interceptor.CommandContext.flushSessions(CommandContext.java:203) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.interceptor.CommandContext.close(CommandContext.java:132) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:113) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:42) ~[camunda-engine-spring-7.8.0.jar:7.8.0]
      	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) ~[spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
      	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:40) ~[camunda-engine-spring-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:66) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:30) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.impl.TaskServiceImpl.complete(TaskServiceImpl.java:173) ~[camunda-engine-7.8.0.jar:7.8.0]
      	at org.camunda.bpm.engine.rest.sub.task.impl.TaskResourceImpl.complete(TaskResourceImpl.java:96) ~[camunda-engine-rest-core-7.8.0.jar:7.8.0]
      	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_74]
      	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_74]
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_74]
      	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_74]
      

      A solution for this issue would be returning a copy of List<IdentityLinkEntity> with the dirty entity in the GetIdentityForTaskCmd.

      Cheers,
      Jürgen

            Assignee:
            Unassigned
            Reporter:
            Jürgen Fritz
            Yana Vasileva Yana Vasileva
            Daniel Kelemen Daniel Kelemen
            Votes:
            4 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated:
              Resolved: