G’day, mates — as they say Down Under, where I happen to be at the moment on a rather large SailPoint engagement. It’s been a while, and I’m sorry for that. I keep promising more, new, and better content and haven’t delivered.
The last couple of months however have been absolutely crazy and there have been some changes on my end, as you perhaps can see. Now that things have shaped up a bit, maybe I can get back to the business at hand here on the blog, again as I have time.
Stupid Pet Tricks
When I was growing up and in college, a famous comedian became famous (partially) by having a segment on his show called “Stupid Pet Tricks.” Some were hilarious and some… belonged on the 1980’s “Gong Show.” (If you’ve never heard of “The Gong Show,” trust me, you aren’t missing anything).
Since that time, I’ve always thought of various developer tricks in the same light. Some are quite slick and useful and some… really just need to be buried. I’ll leave it to you to decide on this one.
Out of sheer laziness, while onboarding SailPoint applications that feature a BuildMap
rule (eg. BuildMap
, JDBCBuildMap
, and SAPBuildMap
), I sometimes utilize a method for “printing debug statements” that I can see directly and immediately in connectorDebug
, without having to jump into or tail the SailPoint IdentityIQ log or application server logs.
It’s also just a bit less verbose as the SailPoint IdentityIQ logs typically have a large class identification prefix in front of them, which can get rather cumbersome and make it more difficult to pick out one’s intended debug output.
Plus I hate changing logging levels in log4j.properties
even though the SailPoint IdentityIQ debug page allows me to load a new logging configuration dynamically. In short, I’m just a lazy, complaining type when it comes to SailPoint IdentityIQ debug statements.
Someone mentioned this would be worth blogging about, so here goes. (At the very least, this is an easy article to write and perhaps will get me back into the blogging swing?!)
__DEBUG__ Schema
Now, I would definitely recommend doing this only on a local or designated sandbox and then making sure you clean up before checking in your code. (You are using some form of source code control for your SailPoint IdentityIQ development, aren’t you?!)
This isn’t rocket science, really. The “Stupid Developer Trick” here is to:
- Add a
__DEBUG__
attribute to the application schema you are trying to debug. - Use
Map.put()
statements to write your info or debug statements into that place in your return map. - When the
ResourceObject
is printed inconnectorDebug
, you can see your info or debug statements immediately.
Of course, none of this helps you at all if you are getting errors in aggregation that prevent you from displaying the ResourceObject
in connectorDebug
, but hey… I labeled this as a “Stupid Developer Trick,” so I’ve already indemnified myself (mate). 🙂
A Sample Scenario
As always, a sample scenario with accompanying code always helps. And that’s what we’re here for anyway — free code, mate.
In my sample scenario, I’ve written a small procedure (called from a library) to transform employee Ids. I just want to debug print the original employee Id and what the transformed employee Id is as a return from that procedure.
Normally this would entail a couple of log.debug()
statements, which usually ends up being log.error()
statements, because I don’t want to change the log levels in log4j.properties
… blah, blah, blah. (And I do mean blah!!)
I just want to see the values when I run in connectorDebug
. Is that too much to ask?!1
So first I’ll add the __DEBUG__
attribute to the existing application schema:
Now, I just update my BuildMap
rule to enable Map.put()
calls to that “attribute”:
// References: // - Aggregation Library // // Imports. import sailpoint.object.Schema; import sailpoint.connector.Connector; import sailpoint.connector.DelimitedFileConnector; // Build the initial map. HashMap map = DelimitedFileConnector.defaultBuildMap( cols, record ); if (schema.getObjectType().compareTo( Connector.TYPE_ACCOUNT ) == 0) { // Get/transform employee ids. String originalEmployeeId = map.get( "employeeId" ); String transformedEmployeeId = getEmployeeId( originalEmployeeId ); map.put( "__DEBUG__", "Original Id: " + originalEmployeeId +" " + "Transformed Id: " + transformedEmployeeId ); map.put( "employeeId", transformedEmployeeId ); } // Return the map. return map;
And now, I’ll just run connectorDebug
and see my debug output directly in the resulting output:
$ cd /path/to/app-server/webapps/iiq55/WEB-INF/bin $ ./iiq console -j Using JLine > connectorDebug "Financial Application" iterate <ResourceObject displayName="JeffMurphy" identity="338" objectType="account"> <Attributes> <Map> <entry key="__DEBUG__" value="Original Id: 1a2c3a4cx Transformed Id: 01A2C3A4CX"/> <entry key="acct_lastLogin" value="04/21/2008 14:30:34"/> <entry key="app2_inactive" value="false"/> <entry key="app2_privileged" value="false"/> <entry key="app2_service" value="false"/> <entry key="dbId" value="338"/> <entry key="employeeId" value="01A2C3A4CX"/> <entry key="groupmbr"> <value> <List> <String>AcctsPayable</String> </List> </value> </entry> <entry key="userName" value="JeffMurphy"/> </Map> </Attributes> </ResourceObject> : : <ResourceObject displayName="RachelStone" identity="258" objectType="account"> <Attributes> <Map> <entry key="__DEBUG__" value="Original Id: 1c2a3b Transformed Id: 00001C2A3B"/> <entry key="acct_lastLogin" value="04/17/2008 21:26:43"/> <entry key="app2_inactive" value="false"/> <entry key="app2_privileged" value="false"/> <entry key="app2_service" value="false"/> <entry key="dbId" value="258"/> <entry key="employeeId" value="00001C2A3B"/> <entry key="groupmbr"> <value> <List> <String>AuditMgmt</String> <String>InternalAudit</String> </List> </value> </entry> <entry key="userName" value="RachelStone"/> </Map> </Attributes> </ResourceObject>
And there you have it. 🙂 I can see the original employee Id and the transformed employee Id right inside of my connectorDebug
output.
Going Into The Bonus Round
Okay, so “meh”… It “works.” Wanna get stupider? We can extend and augment this slightly to provide multi-lined debug statements.
First, we’ll mark the __DEBUG__
attribute in the application schema as “Multi-Valued”. This will force SailPoint IdentityIQ to expect an ArrayList
for the __DEBUG__
attribute and we’ll just List.add()
and create a multi-line debug slot in the resulting ResourceObject
that we can write whatever we want into on “separate lines”:
And the new code to leverage the multi-valued __DEBUG__
“attribute”:
// References: // - Aggregation Library // // Imports. import sailpoint.object.Schema; import sailpoint.connector.Connector; import sailpoint.connector.DelimitedFileConnector; // Build the initial map. HashMap map = DelimitedFileConnector.defaultBuildMap( cols, record ); if (schema.getObjectType().compareTo( Connector.TYPE_ACCOUNT ) == 0) { // Get/transform employee ids. String originalEmployeeId = map.get( "employeeId" ); String transformedEmployeeId = getEmployeeId( originalEmployeeId ); // Completely over-the-top support for multi-lined debugging. :-) List debug = map.get( "__DEBUG__" ); if (debug == null) debug = new ArrayList(); debug.add( "Original Id: " + originalEmployeeId ); debug.add( "Transformed Id: " + transformedEmployeeId ); debug.add( "Stupid Developer Tricks - Will They Make Me Famous?! (Prob not... :-))" ); map.put( "__DEBUG__", debug ); map.put( "employeeId", transformedEmployeeId ); } // Return the map. return map;
And our bonus round output from connectorDebug
:
> connectorDebug "Financial Application" iterate <ResourceObject displayName="JeffMurphy" identity="338" objectType="account"> <Attributes> <Map> <entry key="__DEBUG__"> <value> <List> <String>Original Id: 1a2c3a4cx</String> <String>Transformed Id: 01A2C3A4CX</String> <String>Stupid Developer Tricks - Will They Make Me Famous?! (Prob not... :-))</String> </List> </value> </entry> <entry key="acct_lastLogin" value="04/21/2008 14:30:34"/> <entry key="app2_inactive" value="false"/> <entry key="app2_privileged" value="false"/> <entry key="app2_service" value="false"/> <entry key="dbId" value="338"/> <entry key="employeeId" value="01A2C3A4CX"/> <entry key="groupmbr"> <value> <List> <String>AcctsPayable</String> </List> </value> </entry> <entry key="userName" value="JeffMurphy"/> </Map> </Attributes> </ResourceObject> : : <ResourceObject displayName="RachelStone" identity="258" objectType="account"> <Attributes> <Map> <entry key="__DEBUG__"> <value> <List> <String>Original Id: 1c2a3b</String> <String>Transformed Id: 00001C2A3B</String> <String>Stupid Developer Tricks - Will They Make Me Famous?! (Prob not... :-))</String> </List> </value> </entry> <entry key="acct_lastLogin" value="04/17/2008 21:26:43"/> <entry key="app2_inactive" value="false"/> <entry key="app2_privileged" value="false"/> <entry key="app2_service" value="false"/> <entry key="dbId" value="258"/> <entry key="employeeId" value="00001C2A3B"/> <entry key="groupmbr"> <value> <List> <String>AuditMgmt</String> <String>InternalAudit</String> </List> </value> </entry> <entry key="userName" value="RachelStone"/> </Map> </Attributes> </ResourceObject>
Clean Up and This Concludes…
Now, again (mate!)… Clean that mess up before the children see it and/or it finds its way into source code control! At least pull the __DEBUG__
attribute from the schema.
The beauty of the rest and how SailPoint IdentityIQ works — that is the code — is that you can actually leave the rest of the “debug code” in your rule and the overall rule logic will still work. (Do you know why? Because if your map contains attributes not defined in the schema… those map values get ignored when the ResourceObject
is built, right?!)
And that concludes this year’s installment of “Stupid Developer Tricks.” Someone thought this was useful and encouraged me to post it. Don’t “gong” the messenger!
G’day to all you mates, from Melbourne, Australia!
——————–
1 – The situation isn’t really an issue with SailPoint IdentityIQ as much as just the way JEE development works in general, mind you.