ClassLoader and 3rd-party libraries in Plugin

Hello Experts,

May I ask is it possible to tell the plugin to use the jar files provided under plugin /lib?
Problem: we have a plugin which has utility to create .xlsx excel report. And we are using the latest version (at the moment on maven, it’s 5.1.0) And there might a chance that customer side also have it’s own poi related jars with older version (e.g. 4.1.0) under ~/WEB-INF/lib, and sometimes this might bring up unexpected errors.

May I ask:

  1. What’s the best practice here regarding the 3rd party libraries come with the plugin?
  2. Is there any way to tell the plugin to use specific libraries?
  3. I have tested put 3rd party libraries in a separate plugin and configure the package under scriptPackages, then I try to call it from the main plugin, however, the class can not be found. Any idea how we I achieve this?

Thanks and Regards,
Mike

I have been waiting on this as well. Support and Community have posted that plugin classloader is isolated from IIQ, but I differ. If the plugin classloader was isolated, then we should not be able to use the SailpointContext within plugin class & the whole concept that plugin class need to implement the Plugin base classes.

Have you tested?

I’m running IdentityIQ 8.3 f4b330b4da3-20220427-175259.

I created a class like this:

package test;
public class Greetings {
	public static int version = 1;
}

Generated Greetings-1.0.jar for it, placed it inside /identityiq/WEB-INF/lib and restarted. Then ran a rule in debug mode:

<Source>
  import test.*;
  return Greetings.version;
</Source>

And I got class not found exception. Apparently not everything in /identityiq/WEB-INF/lib gets added to classpath.

So instead I placed Greetings-1.0.jar in /tomcat9.0/lib and restarted. That worked:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Integer PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Integer>1</Integer>

Then I updated Greetings.jar to version 2. This Greetings-2.0.jar I did NOT copy to /identityiq/WEB-INF/lib nor /tomcat9.0/lib.

Instead I created a plugin, referenced Greetings-2.0.jar. I packaged Greetings-2.0.jar inside /lib folder, like so:

Plugin.zip
    \lib
        \Greetings-2.0.jar
    \[plugin package]
        \[plugin classes]

In my plugin class I exposed a simple REST:

    @POST
	@Path("test")
	public Response test() {
		return Response.ok().entity(Greetings.version).build();
	}

I ran the rule, I got 1. I called the plugin’s REST API, I got 2. It seems the plugin class loader has precedence over IdentityIQ classloader.

I’ve been putting 3rd party JARs (MyBatis, Log4J, Jackson XML, etc.) inside a plugin ZIP’s lib folder since IdentityIQ 7.2, never had any issues finding the classes. Though the libraries loading configuration files do not work (e.g. MyBatis and Log4j), the workaround is to initialize them programmatically when plugin is first loaded.

So just put the JAR inside plugin’s lib folder and it should work. Did you see a different behavior?

2 Likes

Hi All,

Just to confirm what KC Wong has said above.

IdentityIQ Plugin’s class loader should be isolated from IdentityIQ’s class loader. This doesn’t mean a Plugin cannot access IdentityIQ API, because it can. The isolation is that IdentityIQ cannot access the Plugin’s classes and libraries without going via the Plugin Class Loader.

Here is a great article on class loaders which may shed some light:

classloaders in a JVM follow a hierarchical model, such that every class loader has a single parent

So, the Plugin Class loader is the child to the IdentityIQ class loader. The child can access the parent, but not the other way around.

Hello Christian and KC Wong,

Thanks a lot for your input. I will retested again on the class loader within our environment.

Besides that, do you know can one plugin access another plugin’s 3rd party libraries or REST APIs? For example, we would like to have a plugin which provide the API for generating excel file with poi libraries. Then other plugins can simply reuse the same api call, instead of store the same poi libraries or java class on its own, do you know whether this can be achieve.

in another word, the child plugin class loader also wants to access its siblings’.

Thanks a lot for your knowledge and input here.

Best regards,

You can expose classes from a plugin by mentioning them in manifest:

<Plugin>
    <Attributes>
        <Map>
            <entry key="scriptPackages">
                <value>
                    <List>
                        <String>Put Package names here (package, not class)</String>
                        <String>com.abc.package1</String>
                    </List>
                </value>
            </entry>
        </Map>
    </Attributes>
</Plugin>

Then you can access them (e.g. inside a rule), using the class PluginsUtil:

sailpoint.plugin.PluginsUtil.instantiate(...)

to obtain an instance of that a class exposed from manifest. IIRC you can also use PluginsUtil from inside a plugin.

Inside BeanShell (rule, workflow, etc.) you can depend on lazy binding and just call the methods on the object instance returned by PluginsUtil.instantiate().

Inside a Plugin you’ll be using Java, so you will have to use Reflection to get access to the methods and members.

1 Like