Kuali Rice Development
  1. Kuali Rice Development
  2. KULRICE-11043

Person document goes into exception when attempting to deactivate a Person with a single group membership

    Details

    • Type: Bug Fix Bug Fix
    • Status: Closed Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 2.3
    • Fix Version/s: 2.3.3
    • Component/s: Development
    • Security Level: Public (Public: Anyone can view)
    • Labels:
      None
    • Similar issues:
      KULRICE-4575IM Person doc should only allow editing of direct group memberships
      KULRICE-8342Person document incorrectly validating "unique" attributes on role memberships
      KULRICE-4368On the Group Document, if you forget to enter description, when you submit and get the validation errors it messes up the membership list
      KULRICE-3898Unable to add a Group to a Person via the Person document - access error received
      KULRICE-8337Adding line to group membership on person record causes NPE
      KULRICE-9199Unable to inactivate roles of type unitHierarchy or unit from person maintenance docs
      KULRICE-7103Person Inquiry Screen doesn't load past the memberships section
      KULRICE-4953KIM Person Document does not handle duplicate active group memberships correctly.
      KULRICE-1365SQL error when doing a Rule Lookup by person reviewer when attempting to search workgroup membership
      KULRICE-12124Submitting a Person document causes a stacktrace even though the document goes final
    • Application Requirement:
      KC
    • KAI Review Status:
      Not Required
    • KTI Review Status:
      Not Required
    • Code Review Status:
      Not Required
    • Include in Release Notes?:
      Yes

      Description

      If you try to deactivate a kuali user by unchecking the Active checkbox, submitting the person document will go into exception if that user has a single group membership. In the logs there is an associated OLE error.
      If the user has 2 or more group memberships then everything works correctly.

      Steps:
      First I took user entity id = 10046, username headley and deactivated two of her 3 group memberships, leaving her with a SINGLE group membership: "proposalAdmin" group, person edit record submission was successful.

      I then opened up a new person edit document for this user (doc # 4140) and tried to completely deactivate this user by unchecking the "Active" checkbox then submitting the document.
      It took a couple of minutes but eventually the document went into exception. I was able to confirm this by doing a doc search for doc# 4140.

      Stack trace
      Below is a snippet of the OLE error I saw in eclipse during local debugging of this issue:
      
      at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      at $Proxy93.doRouteStatusChange(Unknown Source)
      at sun.reflect.GeneratedMethodAccessor496.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:318)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
      at org.kuali.rice.core.framework.persistence.jpa.NoResultExceptionInterceptor.invoke(NoResultExceptionInterceptor.java:33)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      at $Proxy94.doRouteStatusChange(Unknown Source)
      at org.kuali.rice.krad.workflow.postprocessor.KualiPostProcessor.doRouteStatusChange(KualiPostProcessor.java:57)
      at org.kuali.rice.kew.engine.StandardWorkflowEngine.notifyPostProcessor(StandardWorkflowEngine.java:526)
      at org.kuali.rice.kew.engine.StandardWorkflowEngine.nodePostProcess(StandardWorkflowEngine.java:433)
      at org.kuali.rice.kew.engine.StandardWorkflowEngine.process(StandardWorkflowEngine.java:150)
      at org.kuali.rice.kew.impl.document.DocumentProcessingQueueImpl.processWithOptions(DocumentProcessingQueueImpl.java:62)
      at sun.reflect.GeneratedMethodAccessor826.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:318)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
      at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      at $Proxy168.processWithOptions(Unknown Source)
      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.kuali.rice.ksb.messaging.MessageServiceInvoker.invokeService(MessageServiceInvoker.java:157)
      at org.kuali.rice.ksb.messaging.MessageServiceInvoker$1.doInTransaction(MessageServiceInvoker.java:72)
      at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
      at org.kuali.rice.ksb.messaging.MessageServiceInvoker.run(MessageServiceInvoker.java:67)
      at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
      at java.lang.Thread.run(Unknown Source)
      Caused by: org.apache.ojb.broker.OptimisticLockException: Object has been modified by someone else: org.kuali.rice.kim.impl.group.GroupMemberBo@123b40e[id=15741,groupId=11009,memberId=12638506,typeCode=P,activeFromDateValue=<null>,activeToDateValue=2013-07-10 09:46:21.273,versionNumber=87,objectId=B2F24618B55BED24E040A8C019E60477,newCollectionRecord=false]
      at org.apache.ojb.broker.accesslayer.JdbcAccessImpl.executeUpdate(Unknown Source)
      at org.apache.ojb.broker.core.PersistenceBrokerImpl.storeToDb(Unknown Source)
      at org.apache.ojb.broker.core.PersistenceBrokerImpl.store(Unknown Source)
      at org.apache.ojb.broker.core.PersistenceBrokerImpl.store(Unknown Source)
      at org.apache.ojb.broker.core.PersistenceBrokerImpl.store(Unknown Source)
      at org.apache.ojb.broker.core.DelegatingPersistenceBroker.store(Unknown Source)
      at org.apache.ojb.broker.core.DelegatingPersistenceBroker.store(Unknown Source)
      at org.springmodules.orm.ojb.PersistenceBrokerTemplate$9.doInPersistenceBroker(PersistenceBrokerTemplate.java:246)
      at org.springmodules.orm.ojb.PersistenceBrokerTemplate.execute(PersistenceBrokerTemplate.java:141
      

      The implementor said he fixed this issue locally by calling the CacheManager and doing a Groupmember cache flush right after the businessObjectService.save call like this

      UiDocumentServiceImpl
      bos.addAll(groupPrincipals);
              bos.addAll(rolePrincipals);
              bos.addAll(roleRspActions);
              bos.addAll(personDelegations);
              // boservice.save(bos) does not handle deleteawarelist
              getBusinessObjectService().save(bos);
      
              // KC-638 KC 5.1 deactivation of user via person maintenance page fails and document goes into exception
              // KC 3.1.1 code was flushing cache for principle added
              // KC 5.1.1 is supposed to do this automatically but doesn't seem to work.  Perhaps race condition
              //          (We should look at how Kuali Team fixes this KCINFR-840 and perhaps change to their method)
              //
              // flush kim caches for items just added
              DistributedCacheManagerDecorator distributedCacheManagerDecorator = GlobalResourceLoader
              .getService(KimApiConstants.Cache.KIM_DISTRIBUTED_CACHE_MANAGER);
              distributedCacheManagerDecorator.getCache(GroupMember.Cache.NAME).clear();
              distributedCacheManagerDecorator.getCache(RoleMember.Cache.NAME).clear();
              distributedCacheManagerDecorator.getCache(DelegateMember.Cache.NAME).clear();
              // KC-638 END
              
              if (!blankRoleMemberAttrs.isEmpty()) {
                  getBusinessObjectService().delete(blankRoleMemberAttrs);
              }
      

      But on researching this issue, I found that the saveEntityPerson method on the UiDocumentService class has the annotations required to flush group and role members already. The only thing it is missing is the delegateMembers but adding this will not fix this issue since the Optimistic Lock Exception has to do with the GroupMember entry. when I looked at the saveEntityPerson method, I notice that the method attempts to save membership changes for the user first and then at the very last checks if the user has been deactivated and if so again gets the role and group memberships and tries to delete them. If the person is going to be deactivated anyway, why do it at the end? We should save time and resources and do it at the very beginning and avoid adding roles, groups etc. So making the following change fixes the issue.

      UiDocumentServiceImpl
      // checking if person being inactivated before we assign roles, groups etc.
       if ( !inactivatingPrincipal ) {
      	        List <GroupMemberBo>  groupPrincipals = populateGroupMembers(identityManagementPersonDocument);
      	        List <RoleMemberBo>  rolePrincipals = populateRoleMembers(identityManagementPersonDocument);
      	        List <DelegateTypeBo> personDelegations = populateDelegations(identityManagementPersonDocument);
      	        List <PersistableBusinessObject> bos = new ArrayList<PersistableBusinessObject>();
      	        List <RoleResponsibilityActionBo> roleRspActions = populateRoleRspActions(identityManagementPersonDocument);
      	        bos.add(kimEntity);
      	        //if(ObjectUtils.isNotNull(kimEntity.getPrivacyPreferences()))
      	        //	bos.add(kimEntity.getPrivacyPreferences());
      	        bos.addAll(groupPrincipals);
      	        bos.addAll(rolePrincipals);
      	        bos.addAll(roleRspActions);
      	        bos.addAll(personDelegations);
      	        // boservice.save(bos) does not handle deleteawarelist
      	        getBusinessObjectService().save(bos);
      	        
      	        List <RoleMemberAttributeDataBo> blankRoleMemberAttrs = getBlankRoleMemberAttrs(rolePrincipals);
      	        if (!blankRoleMemberAttrs.isEmpty()) {
      	            getBusinessObjectService().delete(blankRoleMemberAttrs);
      	        }
      	    } else {
      	        KimImplServiceLocator.getRoleInternalService().principalInactivated(
                          identityManagementPersonDocument.getPrincipalId());
      	    }
      

      It is possible that we could avoid overhead by excluding more actions by checking if the user is set for deactivation further up in the code, I have not checked that yet.

        Activity

        Hide
        Gayathri Athreya added a comment - - edited

        KTI resolution was to go with whatever Peter Giles thinks is right and based on the last discussion we had, he is Ok with the inactiveMember check.

        Show
        Gayathri Athreya added a comment - - edited KTI resolution was to go with whatever Peter Giles thinks is right and based on the last discussion we had, he is Ok with the inactiveMember check.
        Hide
        Gayathri Athreya added a comment -

        Should be fixed now.

        Show
        Gayathri Athreya added a comment - Should be fixed now.
        Hide
        Gayathri Athreya added a comment -

        Tested and confirmed working.

        Show
        Gayathri Athreya added a comment - Tested and confirmed working.

          People

          • Assignee:
            Gayathri Athreya
            Reporter:
            Gayathri Athreya
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Structure Helper Panel