Leaver workflow is failing

Which IIQ version are you inquiring about?

Version 8.3

Share all details related to your problem, including any error messages you may have received.

Hello,

Leaver event workflow de-provisioning has been implemented for various applications as follows.

App1 and app2 - Disable operation.

App3 and App4 - Delete operation.

App5 and App6 - Disable and remove entitlements.

Whenever the identity has app3 or App4 application accounts, those workflow case is failing with below error and this halting to rest workflow process execution.

An unexpected error occurred: A different object with the same identifier value was already associated with the session : [sailpoint.object.Link#0s54***************] - Id referring to app3 or app4 link Id and we can

We are compiling the plan with below arguments.

Set to true to enable optimistic provisioning. This will cause changes to the entitlements compiled from role assignments to be applied immediately to the identity cube rather than waiting for the next refresh/reaggregation after the provisioning system completes the request.

<Step action=“call:compileProvisioningProject” icon=“Default” name=“Compile Plan” " resultVariable=“project”>










Any help would be highly appreciated.

Thanks

Hi Sri,
Welcome to Sailpoint Developer Community. Would be great if you could share the workflow so we coyld take a look on that.

2 Likes

Are you using custom workflow ? can you share plan .

also check if you using context.decache() on the link object somewhere during the processing , which might generate these errors " A different object with the same identifier value was already associated with the session : [sailpoint.object.Link#0s54*"

Hi Vishal,

We are using custom workflow and building the plan with below logic.

		import java.util.List;
          import sailpoint.object.Attributes;
          import sailpoint.object.Custom;
          import sailpoint.object.ProvisioningPlan;
          import sailpoint.object.ProvisioningPlan.AccountRequest;
          import sailpoint.object.QueryOptions;
          import sailpoint.object.Entitlement;
          import sailpoint.api.SailPointContext;
          import sailpoint.api.IncrementalObjectIterator;
          import sailpoint.api.IdentityService;
          import sailpoint.object.Identity;
          import sailpoint.object.Link;
          import sailpoint.object.Application;
          import sailpoint.object.Filter;
          import java.util.Map;

          import sailpoint.tools.Util;
          import org.apache.log4j.Logger;
          import org.apache.log4j.Level;
          import org.apache.commons.collections.CollectionUtils;
          import org.apache.commons.logging.Log;
          import org.apache.commons.logging.LogFactory;

          static Log logger = LogFactory.getLog("Workflow.ABCD-Workflow-Leaver");
       
          ProvisioningPlan newPlan = new ProvisioningPlan();
          newPlan.setNativeIdentity(plan.getNativeIdentity());
          List deleteAppsList = new ArrayList();
          List disableAppsList = new ArrayList();
          List logicalAppsList = new ArrayList();

          Custom customAttr = context.getObjectByName(Custom.class, "ABCD-Leaver-Apps");
          if (null != customAttr) {
            Attributes attr = (Attributes) customAttr.getAttributes();
            deleteAppsList = attr.getList("deleteApps");
            disableAppsList = attr.getList("disableApps");
            logicalAppsList = attr.getList("logicalApps");
          }
          if(identityName != void &amp;&amp; identityName != null) {
            Identity identity = context.getObjectByName(sailpoint.object.Identity.class, identityName);
            List accountRequests = plan.getAccountRequests();
            if(CollectionUtils.isNotEmpty(accountRequests)) {
              for (AccountRequest accntReq : accountRequests ) {
                QueryOptions queryOptionsAccount = new QueryOptions();
                String applicationName = accntReq.getApplication();
                String nativeIdentity = accntReq.getNativeIdentity();
                queryOptionsAccount.addFilter(Filter.eq("application.name", applicationName));
                queryOptionsAccount.addFilter(Filter.eq("nativeIdentity", nativeIdentity));
                IncrementalObjectIterator linkIter = new IncrementalObjectIterator(context, Link.class, queryOptionsAccount);

                String isDisabled = "";
                while (linkIter.hasNext()) {
                  Link linkVal = linkIter.next();
                  isDisabled = Boolean.toString(linkVal.isDisabled());
                  break;
                }
                Util.flushIterator(linkIter);
                logger.debug("isDisabled ::" +isDisabled);

                if(isDisabled.equalsIgnoreCase("false") || logicalAppsList.contains(applicationName)){
                  AccountRequest newAcctReqForSnow = new AccountRequest();
                  logger.debug(" Termination Applications name==== "+ applicationName);
                  newAcctReqForSnow.setNativeIdentity(nativeIdentity);
                  newAcctReqForSnow.setApplication(applicationName);
                  if (deleteAppsList.contains(applicationName)) {
                    newAcctReqForSnow.setOperation(ProvisioningPlan.AccountRequest.Operation.Delete);
                    newPlan.add(newAcctReqForSnow);
                  } else if (disableAppsList.contains(applicationName)) {
                    newAcctReqForSnow.setOperation(ProvisioningPlan.AccountRequest.Operation.Disable);
                    newPlan.add(newAcctReqForSnow);
                  } else if (logicalAppsList.contains(applicationName)) {
                    String logicalAppName = applicationName;
                    IdentityService identityService = new IdentityService(context);
                    Application logicalAppObject = context.getObjectByName(Application.class,logicalAppName);
                    List logicalAppLinks = identityService.getLinks(identity,logicalAppObject);
                    logger.debug("Terminated user's logicalAppLinks  is "+logicalAppLinks);
                    if(null != logicalAppLinks &amp;&amp; !logicalAppLinks.isEmpty()) {
                      for (Link link : logicalAppLinks) {
                        AccountRequest acctReq = new AccountRequest();
                        acctReq.setApplication(link.getApplicationName());
                        acctReq.setInstance(link.getInstance());
                        acctReq.setNativeIdentity(link.getNativeIdentity());
                        acctReq.setOperation(AccountRequest.Operation.Disable);
                        logger.debug("Account Operation "+acctReq);
                        List entitlements = link.getEntitlements(Locale.US, null);
                        logger.debug("Entitlement Operation "+entitlements);
                        if(null != entitlements &amp;&amp; !entitlements.isEmpty()) {
                          for(Entitlement ent : entitlements) {
                            String attName  = ent.getAttributeName();
                            String attValue = ent.getAttributeValue();
                            AttributeRequest attReq = new AttributeRequest(attName, ProvisioningPlan.Operation.Remove, attValue);
                            acctReq.add(attReq);
                          }
                        }
                        newPlan.add(acctReq);
                      }
                    }
                    context.decache(logicalAppObject);
                  } else if (("ITIL".equalsIgnoreCase(applicationName))){
                    AttributeRequest attrReq = new AttributeRequest();
                    newAcctReqForSnow.setOperation(ProvisioningPlan.AccountRequest.Operation.Modify);
                    newAcctReqForSnow.add(new AttributeRequest("active",ProvisioningPlan.Operation.Set,"false"));
                    newAcctReqForSnow.add(new AttributeRequest("locked_out",ProvisioningPlan.Operation.Set,"true"));
                    newPlan.add(newAcctReqForSnow);

                  }
                }
              }
            }

            String pAmApp = "Application3";
            IdentityService identityService = new IdentityService(context);
            Application pAppObject = context.getObjectByName(Application.class,pAmApp);
            List adLinks = identityService.getLinks(identity,pAppObject);
         
            if(null != adLinks &amp;&amp; !adLinks.isEmpty()) {
              for (Link link : adLinks) {
                AccountRequest acctReq = new AccountRequest();
                
                acctReq.setApplication(link.getApplicationName());
                acctReq.setInstance(link.getInstance());
                acctReq.setNativeIdentity(link.getNativeIdentity());
                acctReq.setOperation(AccountRequest.Operation.Modify);
              
                logger.debug("Account Operation "+acctReq);
                List entitlements = link.getEntitlements(Locale.US, null);
                logger.debug("Entitlement Operation "+entitlements);
                if(null != entitlements &amp;&amp; !entitlements.isEmpty()) {
                  for(Entitlement ent : entitlements) {
                    String attName  = ent.getAttributeName();
                    String attValue = ent.getAttributeValue();
                    AttributeRequest attReq = new AttributeRequest(attName, ProvisioningPlan.Operation.Remove, attValue);
                    acctReq.add(attReq);
                  }
                }
                newPlan.add(acctReq);
               
              }
            }
            context.decache(pAppObject);
           
            context.decache(identity);
          }
          return newPlan;

Hello,

according to most of the posts on Compass (e.g. https://community.sailpoint.com/t5/IdentityIQ-Forum/Aggregation-Issues-Reason-a-different-object-with-the-same/m-p/184716), we should decache the Link object before load it again. After looking the code, I just suspect in the below code:

IncrementalObjectIterator linkIter = new IncrementalObjectIterator(context, Link.class, queryOptionsAccount);

String isDisabled = "";
while (linkIter.hasNext()) {
  Link linkVal = linkIter.next();
  isDisabled = Boolean.toString(linkVal.isDisabled());
  break;
}
Util.flushIterator(linkIter);

maybe we can consider decache the Link object, as we also load the Links again in the following method, maybe the Link there is already loaded in the above code?

List logicalAppLinks = identityService.getLinks(identity,logicalAppObject);
List adLinks = identityService.getLinks(identity,pAppObject);

In addition, just wondering why we would like to use IncrementalObjectIterator to retrieve the Link, why not just use below method:

Link link = IdentityService.getLink(Identity identity, Application application, java.lang.String instance, java.lang.String nativeIdentity);
boolean isDisabled = link.isDisabled();
context.decache(link);

hopefully this may fix you issue.

2 Likes

Hi and Hello,
The error you’re encountering in your SailPoint IdentityIQ workflow, related to the message “A different object with the same identifier value was already associated with the session,” is often caused by Hibernate session conflicts. This occurs when there are attempts to use an entity that was already loaded in a different context or session within the same transaction, particularly in batch or complex operations like those in a provisioning plan.

  1. Session Management and Object Identity:
    Ensure that any object (like Link or Identity) retrieved from the database is managed properly. If an object is fetched and then modified, make sure that any changes are flushed and the session is cleared if necessary to avoid conflicts. This is crucial when working with batch operations or loops where the same object may be processed multiple times.
  2. Decaching Strategy:
    It looks like you’re already attempting to decache objects (context.decache(logicalAppObject)), which is good practice in long-running operations involving multiple transactions. However, make sure that this is done judiciously to avoid unintended side effects. Decaching should be done just before the object’s updated version is required or after significant changes have been committed to ensure the session does not hold stale objects.
  3. Optimistic Locking:
    Your setup mentions optimistic provisioning. Ensure that the entities involved in your transactions are configured to support optimistic locking if applicable. This can prevent conflicts by ensuring that the last write wins, and it also provides better error handling capabilities when concurrent modifications occur.

Regards,
Adam

5 Likes

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.