cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Context.search: Care and feeding of iterators

Context.search: Care and feeding of iterators

When looking for specific objects in IdentityIQ, there are a number of options available. We often advise people to use the context.search() method of the sailpoint.api.SailPointContext class to accomplish this. This method offers a number of advantages, most notably the ability to do projection queries. This allows you to only pull the database columns that are actually needed for the logic you are performing. This has advantages by having faster queries and taking up a smaller memory footprint.

 

For example, the following code would get all the ids for all the Link objects that come from the Application, "Active_Directory"

 

QueryOptions qo = new QueryOptions();

qo.addFilter(Filter.eq("application.name","Active_Directory");

Iterator iter = context.search(Link.class, qo, "id");

 

while (iter.hasNext()){

     Object[] row = (Object[]) iter.next();

     String id = row[0];

     // do logic with the id of the object, such as fetch the object do something with it

}

 

This approach, however, can leave open cursors on the database connection when the "while" loop is broken before the iterator has been exhausted. For example, you may be looking for an appropriate or specific identity cube in a correlation rule, and once you find it, break the loop and return the map with the proper identity information.

 

To address this issue, the best practice is to use the method, Util.flushIterator() from the sailpoint.tools.Util class when you are done with your iterator. This ensures that the iterator gets correctly closed out and the database cursor is released. Correcting the example above to account for this, the code would become:

 

QueryOptions qo = new QueryOptions();

qo.addFilter(Filter.eq("application.name","Active_Directory");

Iterator iter = context.search(Link.class, qo, "id");

 

while (iter.hasNext()){

     Object[] row = (Object[]) iter.next();

     String id = row[0];

     // do logic with the id of the object, such as fetch the object and do something with it

}

 

Util.flushIterator(iter);

 

This will ensure rule exits and leaves the JDBC connection of the SailPointContext in a tidy state by preventing a leak of open cursor objects.

 

As a best practice all beanshell code that calls context.search() should either ensure that all rows returned from the iterator are processed or call the flushIterator() method shown above.  There is no penalty for calling flushIterator() on an exhausted iterator.  We recommend using a flushIterator() call in all places where a search() is performed.  The most correct way to approach this problem is to use try/catch/finally blocks to handle closure of the Iterator in all circumstances, as is shown in lines 35 through 37 of the following example:

 

QueryOptions qo = new QueryOptions();

// Abbreviated for clarity's sake.

Iterator it = context.search(Link.class, qo);

 

// Use a try/finally for the rare condition that link getters throw errors.

try {

   

    while (it.hasNext()) {

       

        Link thisLink = (Link) it.next();

       

        // Logic to detect if we have the aberrant condition of two Link

        // records that match the same Application and nativeIdentity.

        // This only happens on systems that have corrupted their Link table.

        // This warning is to add a "canary to the coal mine"

        if (null != matchedLink) {

           

            String wMsg = "Two Link objects match for Application [" +

                appObj.getName() + "] and nativeIdentity [" + acctName + "], ";

            wMsg += " link:" + matchedLink.getId() +

                    " and link:" + thisLink.getId();

            log.error(wMsg);

           

            // If multiple Links match (there should only be one!) then be nice

            // to the cache and cleanup the matches we are throwing away.

            context.decache(matchedLink);

        }

                   

        matchedLink = thisLink;               

    }

} catch (Exception ex) {       

 

   log.error("Exception while processing Link matches", ex);       

  

} finally {

 

    sailpoint.tools.Util.flushIterator(it);

    context.decache(appObj); // Be nice to the Hibernate cache.

   

}

Labels (2)
Comments

Thanks, this really helped me. I had severe errors because of to many open cursors in Oracle. This fixed it.

Ohhh ... I can haz this in the API docs, pleeze???? ... *goes back to fix a gazillion places*

Can we do projection search in dependent objects (ex: RoleAssignment)?

You can only do any kind of search on first class citizens that have a hibernate mapping.

In case of RoleAssignment objects that is not the case.

Your best bet would be to search in IdentityEntitlement objects of type null for attribute assignedRoles to find the right Identity, then load that and potentially double-check the RoleAssignment.

Hi tonmay.bhattacharjee,

Were you able to achieve this ?

I cannot remember exactly how I did.

Hello Experts, 

May I ask what would be the case of IdIterator ? should we also flush the IdIterator or not necessary? 

This article was really helpful to rid out the open cursor issue in custom report.

Version history
Revision #:
2 of 3
Last update:
‎Jul 03, 2023 11:54 AM
Updated by:
 
Contributors