September 27, 2024

IIQCommon quick snippets: Lambda wrappers

This article is part of a blog series providing an overview of the many SailPoint IIQ utilities and tools available in Instrumental ID’s open-source iiq-common-public library. (For an overview of the entire library, see this earlier post.)

This quick post will be quick snippets of the various wrappers across the library. These are decorators, intended to be used with Java lambdas. The decorator does its thing, and then invokes your code. These can usually be nested.

				
					Utilities.whatever((args) -> {
  // Your code here
});
				
			

Metered

SailPoint IIQ’s excellent Meter API allows you to check the performance of your own code, or, in many cases, code shipped with IIQ itself. Meter times a block of code between an “enter” and an “exit” API all. Out-of-box, the API is used as follows:

				
					// Without IIQCommon

Meter.enterByName("Some meter name");
try {
  // The code you want to time
} finally {
  Meter.exitByName("Some meter name");
}
				
			

We noticed, over the years of writing IIQ code, that we frequently had to rename our meters. The name of the class or concept may change, or we may simply decide to restructure the meter names for better display. Using Meter, you have to remember to change both the start and end, and it’s easy to forget or mistype one or the other.

The Metered class uses a callback to do the same. There are two variants, one that returns a value and one that does not. Java is usually good at figuring out which one you mean at compile time.

All you need to remember is your own business logic.

				
					// With IIQCommon

Metered.meter("Some meter name", () -> {
  // The code you want to time
});

String output = Metered.meter("Some other meter", () -> {
  // Your code that returns stuff
  return "Hello world";
});
				
			

Utilities.withPrivateContext

It is common in more advanced IIQ coding to create private SailPointContexts, separate from the main context used by whatever process you’re in.

Two very common examples include

Multi-threaded contexts: SailPointContexts are thread-specific, because they are essentially thin wrappers around Hibernate. When you’re running items in a thread pool, re-using another thread’s SailPointContext can lead to lock contention and other persistence problems. The best practice is to create a new context per thread.

Auditing: Each context is an autonomous transaction. I frequently need to log an AuditEvent in a private context so that it commits properly, even if the whole operation fails and rolls back. We need to be able to trace that User A tried to disable User B, even if the attempt failed.

Without IIQCommon, you would do something like this:

				
					SailPointContext previousContext = SailPointFactory.pushContext();
SailPointContext privateContext = SailPointFactory.getCurrentContext();

try {
  // Do your thing with the private context
} finally {
  if (privateContext != null) {
    SailPointFactory.releaseContext(privateContext);
  }
  if (previousContext != null) {
    SailPointFactory.setContext(previousContext);
  }
} 
				
			

With IIQCommon, all of the boilerplate is done for you, leaving you to implement the business logic. As with Metered, there are two versions: one that does not return a value and one that does.

				
					Utilities.withPrivateContext((privateContext) -> {
  // Do your thing with the private context
});

Object someValue = Utilities.withPrivateContext((privateContext) -> {
  // Do your thing with the private context
  return someResult;
});
				
			
				
					return Metered.meter("Whatever meter", 
  () -> Utilities.withPrivateContext(
          (privateContext) -> {
            // Do your thing here, return some value
          }
        )
);
				
			

What about Beanshell?

Well, it’s a bit harder in Beanshell, which doesn’t support lambda syntax. That’s one reason I prefer writing my IIQ code as compiled Java. However, Beanshell does support anonymous inner classes, which work about the same.

Metered actually uses two “functional interfaces”, one for the version which returns a value and one for the version which does not: MeterCallback and MeterCallbackWithOutput. You can construct anonymous instances of these in Beanshell.

				
					Metered.meter("Some meter", new MeterCallback() {
  public void run() {
    // Do your thing here
  }
});
				
			

There is yet another option.

Since Beanshell is a dynamic scripting language, it can dynamically call a method of your choosing by name using a bsh.This reference.

We offer a variant of withPrivateContext that will invoke the Beanshell method of your choosing, passing the private context as its sole parameter.

Conveniently, Beanshell methods can also access global variables.

Your Rule or Script might look as follows, illustrating both the call and the access to a global value.

				
					public void someMethod(SailPointContext privateContext) {
  Identity joe = privateContext.getObject(Identity.class, identityName);
  // Do something with joe here
}

// This is a global and will be accessible in the method
String identityName = "joe.smith";

// Invokes the 'someMethod' method
Utilities.withPrivateContext(this, "someMethod");