Uploaded image for project: 'Kuali Rice Development'
  1. Kuali Rice Development
  2. KULRICE-2333

Mass action ActionItem deletion occurs before action invocation

    Details

    • Type: Bug Fix
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 0.9.2.1
    • Fix Version/s: 1.0
    • Component/s: Development
    • Labels:
      None
    • Rice Module:
      KEW

      Description

      It appears that upon taking a mass action via the Action List, deletion of ActionItems precedes actual invocation of the Action. This can cause a problem with outbox handling, as outbox items are only created for items for which the user has taken action. This condition is necessarily false if the action has not yet been invoked at the time of action item deletion and therefore items taking action on via mass actions will not be saved into the outbox.

      public void takeMassActions(WorkflowUser user, List actionInvocations) {
      for (Iterator iterator = actionInvocations.iterator(); iterator.hasNext() {
      ActionInvocation invocation = (ActionInvocation) iterator.next();
      ActionItem actionItem = KEWServiceLocator.getActionListService().findByActionItemId(invocation.getActionItemId());
      if (actionItem == null)

      { LOG.warn("Could not locate action item for the given action item id [" + invocation.getActionItemId() + "], not taking mass action on it."); continue; }

      KEWServiceLocator.getActionListService().deleteActionItem(actionItem); // <-- Action not yet invoked...actionTaken == null
      ActionInvocationService actionInvocService = MessageServiceNames.getActionInvocationProcessorService(actionItem.getRouteHeader());
      actionInvocService.invokeAction(user, actionItem.getRouteHeaderId(), invocation);
      // ActionInvocationProcessor.queueActionInvocation(user, actionItem.getRouteHeaderId(), invocation);
      }
      }

      Possible solution: defer action item deletion until after/as part of action invocation

        Attachments

          Activity

          Hide
          ewestfal Eric Westfall added a comment -

          In order for action items to exist you must route a document so that it enters users' action lists. You can see some of the other tests in OutboxTest which have done this. Mainly, this is done using the "WorkflowDocument" API class. Thanks.

          Show
          ewestfal Eric Westfall added a comment - In order for action items to exist you must route a document so that it enters users' action lists. You can see some of the other tests in OutboxTest which have done this. Mainly, this is done using the "WorkflowDocument" API class. Thanks.
          Hide
          smummadi Srikanth Mummadi (Inactive) added a comment -

          Hi,

          I have modified the method testTakeMassActions in OutboxTest

          public void testTakeMassActions() throws Exception {
          final String rkirkendPrincipalId = getPrincipalIdForName("rkirkend");
          List<String> recipients = new ArrayList<String>();
          recipients.add(rkirkendPrincipalId);
          TestRuleAttribute.setRecipientPrincipalIds("TestRole", "qualRole", recipients);
          turnOnOutboxForUser(rkirkendPrincipalId);

          WorkflowDocument document = new WorkflowDocument(getPrincipalIdForName("quickstart"), "TestDocumentType");
          document.routeDocument("");

          document = new WorkflowDocument(rkirkendPrincipalId, document.getRouteHeaderId());
          assertTrue("approve should be requested", document.isApprovalRequested());

          document = new WorkflowDocument(getPrincipalIdForName("user1"), "OutboxTestDocumentType");
          document.routeDocument("");

          document = new WorkflowDocument(rkirkendPrincipalId, document.getRouteHeaderId());
          assertTrue("approve should be requested", document.isApprovalRequested());

          List actionList = new ArrayList(KEWServiceLocator.getActionListService().getActionList(rkirkendPrincipalId, new ActionListFilter()));
          List invocations = new ArrayList();
          ActionToTake actionToTake = new ActionToTake();
          ActionItem actionItem = new ActionItem();

          for (Iterator iterator = actionList.iterator(); iterator.hasNext()

          { ActionItem actinItem = (ActionItem) iterator.next(); actionToTake.setActionItemId(actinItem.getActionItemId()); actionToTake.setActionTakenCd(actinItem.getActionRequestCd()); invocations.add(new ActionInvocation(actinItem.getActionItemId(), actionToTake.getActionTakenCd())); }

          KEWServiceLocator.getWorkflowDocumentService().takeMassActions(rkirkendPrincipalId, invocations);
          assertEquals("Wrong number of outbox items found for rkirkendPrincipalId", 2, KEWServiceLocator.getActionListService().getOutbox(rkirkendPrincipalId, new ActionListFilter()).size());

          }

          In WorkflowDocumentServiceImpl.takeMassActions actionItem was getting deleted before it is saved into the outbox table.
          So,I have commented the method call in WorkflowDocumentServiceImpl.takeMassActions as follows

          public void takeMassActions(String principalId, List actionInvocations) {
          KimPrincipal principal = loadPrincipal(principalId);
          for (Iterator iterator = actionInvocations.iterator(); iterator.hasNext() {
          ActionInvocation invocation = (ActionInvocation) iterator.next();
          ActionItem actionItem = KEWServiceLocator.getActionListService().findByActionItemId(invocation.getActionItemId());
          if (actionItem == null)

          { LOG.warn("Could not locate action item for the given action item id [" + invocation.getActionItemId() + "], not taking mass action on it."); continue; }

          // KEWServiceLocator.getActionListService().deleteActionItem(actionItem);
          ActionInvocationService actionInvocService = MessageServiceNames.getActionInvocationProcessorService(actionItem.getRouteHeader());
          actionInvocService.invokeAction(principalId, actionItem.getRouteHeaderId(), invocation);
          // ActionInvocationProcessor.queueActionInvocation(user, actionItem.getRouteHeaderId(), invocation);
          }
          }

          actionInvocService.invokeAction method internally calls ActionRequestServiceImpl.deactivateRequest which calls deleteActionItems(actionRequest).

          The method call to deleteActionItems(actionRequest) has been commented to resolve the following exception.

          org.springmodules.orm.ojb.OjbOperationException: OJB operation failed; nested exception is org.apache.ojb.broker.OptimisticLockException: Object has been modified or deleted by someone else
          Caused by: org.apache.ojb.broker.OptimisticLockException: Object has been modified or deleted by someone else
          at org.apache.ojb.broker.accesslayer.JdbcAccessImpl.executeDelete(JdbcAccessImpl.java:119)
          at org.apache.ojb.broker.core.PersistenceBrokerImpl.doDelete(PersistenceBrokerImpl.java:583)
          at org.apache.ojb.broker.core.PersistenceBrokerImpl.delete(PersistenceBrokerImpl.java:512)
          at org.apache.ojb.broker.core.PersistenceBrokerImpl.delete(PersistenceBrokerImpl.java:525)
          at org.apache.ojb.broker.core.DelegatingPersistenceBroker.delete(DelegatingPersistenceBroker.java:215)
          at org.apache.ojb.broker.core.DelegatingPersistenceBroker.delete(DelegatingPersistenceBroker.java:215)
          at org.springmodules.orm.ojb.PersistenceBrokerTemplate$10.doInPersistenceBroker(PersistenceBrokerTemplate.java:255)
          at org.springmodules.orm.ojb.PersistenceBrokerTemplate.execute(PersistenceBrokerTemplate.java:141)
          at org.springmodules.orm.ojb.PersistenceBrokerTemplate.delete(PersistenceBrokerTemplate.java:253)
          at org.kuali.rice.kew.actionitem.dao.impl.ActionItemDAOOjbImpl.deleteActionItem(ActionItemDAOOjbImpl.java:59)
          at org.kuali.rice.kew.actionlist.service.impl.ActionListServiceImpl.deleteActionItem(ActionListServiceImpl.java:102)
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          at java.lang.reflect.Method.invoke(Unknown Source)
          at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)
          at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)
          at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
          at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:203)
          at $Proxy204.deleteActionItem(Unknown Source)
          at org.kuali.rice.kew.routeheader.service.impl.WorkflowDocumentServiceImpl.takeMassActions(WorkflowDocumentServiceImpl.java:412)

          I have commited the following changed files to the repository.

          org.kuali.rice.kew.actionlist.OutboxTest and
          org.kuali.rice.kew.routeheader.service.impl.WorkflowDocumentServiceImpl.

          Show
          smummadi Srikanth Mummadi (Inactive) added a comment - Hi, I have modified the method testTakeMassActions in OutboxTest public void testTakeMassActions() throws Exception { final String rkirkendPrincipalId = getPrincipalIdForName("rkirkend"); List<String> recipients = new ArrayList<String>(); recipients.add(rkirkendPrincipalId); TestRuleAttribute.setRecipientPrincipalIds("TestRole", "qualRole", recipients); turnOnOutboxForUser(rkirkendPrincipalId); WorkflowDocument document = new WorkflowDocument(getPrincipalIdForName("quickstart"), "TestDocumentType"); document.routeDocument(""); document = new WorkflowDocument(rkirkendPrincipalId, document.getRouteHeaderId()); assertTrue("approve should be requested", document.isApprovalRequested()); document = new WorkflowDocument(getPrincipalIdForName("user1"), "OutboxTestDocumentType"); document.routeDocument(""); document = new WorkflowDocument(rkirkendPrincipalId, document.getRouteHeaderId()); assertTrue("approve should be requested", document.isApprovalRequested()); List actionList = new ArrayList(KEWServiceLocator.getActionListService().getActionList(rkirkendPrincipalId, new ActionListFilter())); List invocations = new ArrayList(); ActionToTake actionToTake = new ActionToTake(); ActionItem actionItem = new ActionItem(); for (Iterator iterator = actionList.iterator(); iterator.hasNext() { ActionItem actinItem = (ActionItem) iterator.next(); actionToTake.setActionItemId(actinItem.getActionItemId()); actionToTake.setActionTakenCd(actinItem.getActionRequestCd()); invocations.add(new ActionInvocation(actinItem.getActionItemId(), actionToTake.getActionTakenCd())); } KEWServiceLocator.getWorkflowDocumentService().takeMassActions(rkirkendPrincipalId, invocations); assertEquals("Wrong number of outbox items found for rkirkendPrincipalId", 2, KEWServiceLocator.getActionListService().getOutbox(rkirkendPrincipalId, new ActionListFilter()).size()); } In WorkflowDocumentServiceImpl.takeMassActions actionItem was getting deleted before it is saved into the outbox table. So,I have commented the method call in WorkflowDocumentServiceImpl.takeMassActions as follows public void takeMassActions(String principalId, List actionInvocations) { KimPrincipal principal = loadPrincipal(principalId); for (Iterator iterator = actionInvocations.iterator(); iterator.hasNext() { ActionInvocation invocation = (ActionInvocation) iterator.next(); ActionItem actionItem = KEWServiceLocator.getActionListService().findByActionItemId(invocation.getActionItemId()); if (actionItem == null) { LOG.warn("Could not locate action item for the given action item id [" + invocation.getActionItemId() + "], not taking mass action on it."); continue; } // KEWServiceLocator.getActionListService().deleteActionItem(actionItem); ActionInvocationService actionInvocService = MessageServiceNames.getActionInvocationProcessorService(actionItem.getRouteHeader()); actionInvocService.invokeAction(principalId, actionItem.getRouteHeaderId(), invocation); // ActionInvocationProcessor.queueActionInvocation(user, actionItem.getRouteHeaderId(), invocation); } } actionInvocService.invokeAction method internally calls ActionRequestServiceImpl.deactivateRequest which calls deleteActionItems(actionRequest). The method call to deleteActionItems(actionRequest) has been commented to resolve the following exception. org.springmodules.orm.ojb.OjbOperationException: OJB operation failed; nested exception is org.apache.ojb.broker.OptimisticLockException: Object has been modified or deleted by someone else Caused by: org.apache.ojb.broker.OptimisticLockException: Object has been modified or deleted by someone else at org.apache.ojb.broker.accesslayer.JdbcAccessImpl.executeDelete(JdbcAccessImpl.java:119) at org.apache.ojb.broker.core.PersistenceBrokerImpl.doDelete(PersistenceBrokerImpl.java:583) at org.apache.ojb.broker.core.PersistenceBrokerImpl.delete(PersistenceBrokerImpl.java:512) at org.apache.ojb.broker.core.PersistenceBrokerImpl.delete(PersistenceBrokerImpl.java:525) at org.apache.ojb.broker.core.DelegatingPersistenceBroker.delete(DelegatingPersistenceBroker.java:215) at org.apache.ojb.broker.core.DelegatingPersistenceBroker.delete(DelegatingPersistenceBroker.java:215) at org.springmodules.orm.ojb.PersistenceBrokerTemplate$10.doInPersistenceBroker(PersistenceBrokerTemplate.java:255) at org.springmodules.orm.ojb.PersistenceBrokerTemplate.execute(PersistenceBrokerTemplate.java:141) at org.springmodules.orm.ojb.PersistenceBrokerTemplate.delete(PersistenceBrokerTemplate.java:253) at org.kuali.rice.kew.actionitem.dao.impl.ActionItemDAOOjbImpl.deleteActionItem(ActionItemDAOOjbImpl.java:59) at org.kuali.rice.kew.actionlist.service.impl.ActionListServiceImpl.deleteActionItem(ActionListServiceImpl.java:102) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:203) at $Proxy204.deleteActionItem(Unknown Source) at org.kuali.rice.kew.routeheader.service.impl.WorkflowDocumentServiceImpl.takeMassActions(WorkflowDocumentServiceImpl.java:412) I have commited the following changed files to the repository. org.kuali.rice.kew.actionlist.OutboxTest and org.kuali.rice.kew.routeheader.service.impl.WorkflowDocumentServiceImpl.
          Hide
          smummadi Srikanth Mummadi (Inactive) added a comment -

          I can not see the link to resolve the issue.

          Show
          smummadi Srikanth Mummadi (Inactive) added a comment - I can not see the link to resolve the issue.
          Hide
          ewestfal Eric Westfall added a comment -

          Hi Srikanth, it looks like your implementation just comments out the code that deletes the action item when mass actions are taken. I think this isn't quite what we want. We want to be able to delete the action item immediately but still have it go into the outbox. I've committed a fix that gives us what we need. Take a look at the following changeset when you get a chance:

          https://test.kuali.org/fisheye/changelog/rice/?cs=6658

          The test you wrote is really helpful, I was able to use it to verify the fix above

          Show
          ewestfal Eric Westfall added a comment - Hi Srikanth, it looks like your implementation just comments out the code that deletes the action item when mass actions are taken. I think this isn't quite what we want. We want to be able to delete the action item immediately but still have it go into the outbox. I've committed a fix that gives us what we need. Take a look at the following changeset when you get a chance: https://test.kuali.org/fisheye/changelog/rice/?cs=6658 The test you wrote is really helpful, I was able to use it to verify the fix above
          Hide
          ewestfal Eric Westfall added a comment -

          Bulk change of all Rice 1.0 issues to closed after public release.

          Show
          ewestfal Eric Westfall added a comment - Bulk change of all Rice 1.0 issues to closed after public release.

            People

            • Assignee:
              smummadi Srikanth Mummadi (Inactive)
              Reporter:
              ahamid Aaron Hamid (Inactive)
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - 2 hours
                2h
                Remaining:
                Remaining Estimate - 2 hours
                2h
                Logged:
                Time Spent - Not Specified
                Not Specified