This blogpost explores debugging custom Java code within IdentityIQ, a crucial skill for (experienced) developers.
Introduction
IdentityIQ is a product which allows lots of customizations to fit the Identity Management and Identity Governance requirements and needs of any organization. Next to configuration and adding BeanShell rule, it is possible to add custom Java classes in jar-files and Java classes via Plugins.
Add compiled Java classes
- Develop Java code and run that from scripts or hooks in the IdentityIQ framework
- Custom Connectors
- Requires a redeployment and restart of the server
- May require additional libraries
Develop a plugin
- IdentityIQ provided plugin framework
- Written in Java/XHTML/JS
- Does not require a restart
- Certified by Sailpoint safeguarding upgradeability
- Supported by the company developing the plugin
Java Platform Debugger Architecture
Debugging of custom Java classes and Java code in Plugins is possible using the Java Platform Debugger Architecture (JPDA) framework. The JDPA will provide insights into the actual state of the Java code during Runtime! It allows viewing of the actual content of each variable and provide the possibility to run Java code statement by statement. This gives a direct insight into the running of the code and will allow to easily find issues/bugs in the code
JDPA is a collection of APIs to debug Java code. It consists of three components:
- JVMTI - Java VM Tool Interface; Defines the debugging services a VM provides.
- JDWP - Java Debug Wire Protocol; Defines the communication between debuggee and debugger processes.
- JDI - Java Debug Interface; Defines a high-level Java language interface which tool developers can easily use to write remote debugger applications.
The debuggee is the combination of the JVM, JVMTI and IdentityIQ plus the custom Java Code (debuggee application).
The Debugger is the combination of JDI and you favorite IDE.
Included in the JVM are APIs to debug Java code running within the JVM. These APIs together are a native interface that helps to inspect the state and to control the execution of applications running in the JRE; also called the Java Virtual Machine Tools Interface (JVMTI) and is part of the Java Platform Debugger Architecture (JPDA)
Debugging using JDPA
For all the example in the remainder of this post the following is used:
- IDE: IntelliJ
- Example java code: IIQ Business Role Report
- IdentityIQ running in Apache Tomcat 9
Ready for Debugging
Before debugging the Java code the code must be compiled and deployed to IdentityIQ and have the code available in an Integrated Development Environment (for example: Eclipse, IntelliJ). The simplest way to have the code compiled and deployed to IdentityIQ is using the Services Standard Build (SSB).
When the java code (jar-file) is deployed and the server has been restarted or when the plugin is imported, the custom java code is available to be used by IdentityIQ (and all components of IdentityIQ). The custom Java code runs in the same Java Virtual Machine (JVM) as IdentityIQ.
Setting up the IDE environment
IntelliJ configuration
Start Intellij and setup the project (git clone of the GitHub - sailpoint-oss/colab-reports: A collection of reports that can be run as-is or modified to suit your needs.)
Setup the JDK and add IdentityIQ lib directory as module to the project.
Add IdentityIQ lib directory to the project
- Open your installed IntelliJ IDEA Project and.
- Go to the File > Project Structure.
- Select Modules at the left panel and select the Dependencies tab.
- Select the + icon and select 1 JARs or Directories option.
- Select the directory
<local IIQ directory>/WEB-INF/lib
- Click on the OK button.
Edit IDE debug configuration (JDI)
To enable the JDI in IntelliJ a Debug Configuration must be added.
- From the Menu goto
Run
and selectEdit Configuration
:
- Add a new
Remote JVM Debug
configuration using the+
icon:
- Give the configuration a
Name
- Set
Host
to hostname (or FQDN or IP-Address) of your IdentityIQ server:
- Remember the value displayed at
Command line arguments for remote JVM:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
Enabling JDWP on the application Server
Tomcat running on Windows
- Open the application
tomcat9w.exe
from the<tomcat>\bin
directory - Switch to the
Java
-tab and athe remembered line in theJava Options:
text box:
- Restart the Tomcat Service (Press
Stop
and thenStart
on theGeneral
-tab)
Tomcat running on Linux/Unix
- Stop the Tomcat Service (
<tomcat>\bin\shutdown.sh
) - Set the
JPDA_ADDRESS
andJPDA_TRANSPORT
environment variables - Start the Tomcat Service with JPDA enabled (
<tomcat>\bin\catalina.sh jpda start
)
Connecting the debugger
- In IntelliJ start the debug-session using
Run
→Debug <Remote JVM Debug configuration name>
- A new Windows will appear in IntelliJ for the debug session and it should state the JPDA session is connected:
Debugging Session
When the debugger in IntelliJ has been connected to the JVM/IdentityIQ a data channel has been created. The JVM (debuggee) generates events (like breakpoints, thread starts, and stops, etc.) and sends them to the debugger (JDI/IntelliJ) via the JDWP protocol. The debugger can also send commands to the JVM to control the execution of the application.
Breakpoints
When debugging an issue a breakpoint must be set on the statement where the issue is or a (few) statement(s) before.
In the image above a breakpoint has been set on the statement which will check if the value of disabled
*is * "true"
.
After setting the breakpoint, start the process which triggers the to be investigated issue. In this example, this is to start creating a report (take a note of the value specified for disabled
).
A breakpoint will halt the thread in the JVM when the JVM is about to start processing the statement. The debugger (IntelliJ) will show the stack-trace and the live values of the active variables.
It is now possible to view the values by “opening” the variables using the >
, evaluating expressions or setting a watch on the variable.
Step Over/Into/Out ro Run to Cursor
It is also possible to advance to the next statement (Step Over, F8
), dive into a function when the statement contains a function (Step Into, F7
), finish the current function when inside a function (Step Out, Shift+F8
) or place your cursor on a future statement and run the code towards the cursor (Run to Cursor, Alt-F9
).
After a few ‘Step Over’-s, the this.baseQueryOptions._filters
does show the filter does indeed show the correct setting to only show the disabled Bundles/roles in the report:
Finish Debugging session
To finish the debug session either:
- Resume the process (Resume the Program,
F9
) - Stop the debugging session (Stop Program,
CTRL+F2
). Stopping will result in an error, but it might be handy if the process started in IdentityIQ will take a long time to finish.
Keep in mind
When resuming the process, the next time the JVM is to execute the statement with a breakpoint it will again halt the processing at the statement.
Live Demo (SailPoint Developer Days 2024)
To watch a live demo of debugging using the JDPA framework and using the same example as used in this blog post, take a look at the Developer Days Presentation: