9.3 Common SailPoint API Objects and Methods
Key Takeaways
SailPointContext provides getObjectByName, getObjectById, getObject, search, countObjects, saveObject, removeObject, commitTransaction, decache, runRule, and sendEmailNotification.
context.search with a property list runs a projection query that returns Object[] rows instead of full objects, which is far more efficient for large result sets.
Filter builds criteria (eq, ne, like, in, and, or, isnull), and QueryOptions carries filters, ordering, distinct, and result limits.
Changes made through saveObject are not permanent until commitTransaction is called, and decache frees memory in long loops.
Access changes should go through a ProvisioningPlan (AccountRequest plus AttributeRequest) run by the Provisioner or a workflow, not direct edits to Link objects.
Common SailPoint API Objects and Methods
Objective 4.7 asks you to know the common SailPoint API objects and methods and how to leverage them. The full API is in the javadocs that ship with every installation, under identityiq_home/doc/javadoc. Exam items focus on a small, high-use set.
SailPointContext: The Gateway
Every rule receives context, a sailpoint.api.SailPointContext:
| Method | Use |
|---|---|
getObjectByName(Class, name) / getObjectById(Class, id) / getObject(Class, idOrName) | Fetch one object |
getObjects(Class, QueryOptions) | Fetch a List (fine for small sets) |
search(Class, QueryOptions) | Stream matches as an Iterator of objects |
search(Class, QueryOptions, List props) | Projection query: an Iterator of Object[] rows holding only the listed properties |
countObjects(Class, QueryOptions) | Count without loading |
saveObject(obj) / removeObject(obj) | Stage a save or delete |
commitTransaction() / rollbackTransaction() | Make staged changes permanent, or undo them |
decache() / decache(obj) | Clear loaded objects from the Hibernate session to free memory |
runRule(rule, Map args) | Run another rule |
sendEmailNotification(EmailTemplate, EmailOptions) | Send an email from code |
encrypt(String) / decrypt(String) | Use the IdentityIQ keystore |
Querying With Filter and QueryOptions
import sailpoint.object.*;
import java.util.*;
QueryOptions qo = new QueryOptions();
qo.addFilter(Filter.and(
Filter.eq("department", "Finance"),
Filter.eq("inactive", false)));
qo.addOrdering("lastname", true);
qo.setResultLimit(500);
List props = Arrays.asList(new String[] {"name", "email", "manager.name"});
Iterator it = context.search(Identity.class, qo, props);
while (it.hasNext()) {
Object[] row = (Object[]) it.next();
String name = (String) row[0];
// ... row[1] is the email, row[2] the manager's name
}
- Filter static methods include
eq,ne,like(with a match mode),in,and,or,not,isnull,notnull, andignoreCase.Filter.compile("department == \"Finance\"")parses a filter string, the same syntax the Advanced Search "view/edit filter source" link shows. - QueryOptions carries filters plus
addOrdering,setDistinct,setResultLimit, andsetFirstRow. The documentation's own form examples build a distinct, ordered projection query to fill a drop-down. - Dot notation reaches related objects:
manager.name,application.name,identity.department. - Only searchable attributes (extended columns, section 2.3) can be used in database filters. Non-searchable attributes are stored in XML and cannot be queried efficiently.
The Core Object Classes
| Class | Represents | Frequently used methods |
|---|---|---|
| Identity | An identity cube | getName, getDisplayName, getFirstname, getEmail, getManager, getAttribute(name), setAttribute, getLinks, getAssignedRoles, getDetectedRoles, isInactive, getCapabilities, getWorkgroups |
| Link | An account | getNativeIdentity, getApplication, getApplicationName, getAttribute, getIdentity, getDisplayName |
| Application | An application definition | getName, getConnector, getAttributeValue, getSchema("account"), isAuthoritative |
| Bundle | A role | getName, getType, getRequirements, getPermits, getInheritance, getProfiles |
| ManagedAttribute | An entitlement or account group in the catalog | getApplication, getAttribute, getValue, getDisplayName, getOwner, isRequestable |
| ResourceObject | Account or group data from a connector during aggregation | getIdentity, getAttribute, put |
| Custom | A key/value configuration object | get(key), put(key, value) for lookup tables used by rules |
Changing Access: ProvisioningPlan
Do not edit Link attributes directly to grant access. Describe the change as a ProvisioningPlan and let IdentityIQ provision it, audit it, and keep the identity cube consistent:
import sailpoint.object.ProvisioningPlan;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.api.Provisioner;
ProvisioningPlan plan = new ProvisioningPlan();
plan.setIdentity(identity);
AccountRequest acct = new AccountRequest(AccountRequest.Operation.Modify,
"Active Directory", null, link.getNativeIdentity());
acct.add(new AttributeRequest("memberOf", ProvisioningPlan.Operation.Add,
"CN=Finance,OU=Groups,DC=acme,DC=com"));
plan.add(acct);
Provisioner p = new Provisioner(context);
p.execute(plan);
AccountRequest operations include Create, Modify, Delete, Enable, Disable, Unlock, and Lock. AttributeRequest operations are Add, Remove, and Set. In a workflow, pass the plan to LCM Provisioning or a provisioning subprocess instead, so that approvals and policy checks still apply.
Helper Classes
sailpoint.api.IdentityService– for example,getLinks(identity, application)finds an identity's accounts on one application.sailpoint.api.ObjectUtil– utilities such asconvertIdsToNames(context, Application.class, ids), used in the documentation's report examples, plus locking helpers for safely updating identities.sailpoint.tools.Util– null-safe helpers such asisNullOrEmpty,isNotNullOrEmpty,otos(object to string),otob(to boolean), and CSV/list conversions.
Two Everyday Recipes
Lookup tables in a Custom object. Instead of hard-coding a department-to-cost-center map in five rules, store it once:
import sailpoint.object.Custom;
Custom map = context.getObjectByName(Custom.class, "ACME Cost Centers");
String cc = (map != null) ? (String) map.get(department) : null;
Sending an email from code. The documentation's Test Email Sending rule shows the pattern: load an EmailTemplate by name, take a copy with deepCopy(context), put template arguments in a Map, wrap the recipient and arguments in EmailOptions, then call context.sendEmailNotification(template, options). With email redirection on (section 3.4), this is a safe way to test templates.
Patterns That Separate Good Code From Outages
- Iterate, do not load everything. Use
search(ideally a projection) instead ofgetObjectsfor large sets. - Commit, then decache, in loops. Call
commitTransaction()after saves anddecache()every few hundred objects, so the session does not grow until memory runs out. - Never commit inside certain rules unless required. Aggregation, correlation, and customization rules run inside IdentityIQ's own transaction. Return data and let the engine save it.
- Handle nulls. Attributes and managers may be missing, so use
Utilhelpers. - Log at debug, not println. The documentation suggests println only for temporary debugging, removed before production. Prefer a named logger (section 14.1).
A rule must list the names and emails of 200,000 identities without loading full Identity objects into memory. Which call is most appropriate?
context.getObjects(Identity.class, qo)
context.search(Identity.class, qo, Arrays.asList("name","email")) and read Object[] rows
context.getObjectByName(Identity.class, "*")
context.countObjects(Identity.class, qo)
A custom task updates an attribute on thousands of identities with context.saveObject(). After it finishes, none of the changes are visible. What is missing?
A call to context.decache() before each save
Running the task with partitioning enabled
Marking the attribute Searchable
A call to context.commitTransaction() so staged saves become permanent
What is the recommended way for custom code to add a user to an Active Directory group?
Build a ProvisioningPlan with an AccountRequest (Modify) and an AttributeRequest (Add) and run it through the Provisioner or a provisioning workflow.
Set the memberOf attribute on the Link object and call saveObject.
Edit the Application object's schema to include the group.
Insert a row into spt_link with SQL.
Which QueryOptions and Filter combination returns active Finance identities ordered by last name?
QueryOptions.setDistinct(true) with Filter.like("Finance")
Filter.or(Filter.eq("department","Finance"), Filter.eq("inactive", false)) with setResultLimit(0)
Filter.and(Filter.eq("department","Finance"), Filter.eq("inactive", false)) plus addOrdering("lastname", true)
Filter.compile("Finance") with setFirstRow(1)
Sections you finish are checked off in the contents.