Trireme classloader isolation not working as expected

Not applicable

Classloader isolation was introduced in Trireme-support using loadJars javascript function for Trireme to use its own classloader and load classes from that classloader at runtime. That functionality however does not seem to be working properly when used within APIGEE message processor (MP). When Trireme code is executed within MP, the classes from the MP classpath take precedence over the same classes that are in the classloader loaded within Trireme using loadJars function.

We tested it by replicating exact same class in 3 jars with just one minor change in each of the copy of the class. The copies had a console logs that would print FIRST Instance, SECOND Instance or THIRD Instance based on which class got loaded/used at runtime.

Copy of the jar that printed FIRST Instance was put in the environment/api folder of API-1. loadJars function was used to load the jar.

Copy of the jar that printed SECOND Instance was put in the environment/api folder of API-2. loadJars function was used to load the jar.

At this point if a test is run for API-1 or API-2 they would both print out FIRST Instance or SECOND Instance correctly based on which API got executed.

Next, we introduce third copy of the jar that prints THIRD Instance in to the custom_jars folder thats part of APIGEE MP's class path and restarted MP. At this point, our expectation is that when we run test with API-1 or API-2 we would still get FIRST Instance and SECOND Instance printed respectively assuming classloader isolation provided by loadJars worked. But it does not do that. Instead, tests run for both API-1 and API-2 prints out THIRD Instance indicating they used class that was loaded from MP's classloader. What could be causing this? Is there any additional configuration setting that we are missing, or is this an issue within loadjars function?

0 5 293
5 REPLIES 5

Not applicable

Interesting test, this helps me to understand the behavior, although it may not be what you want.

It sort of makes sense to me and perhaps the term ought to be "as needed classloader" rather than "isolation". So my interpretation is, if trireme already has the jars loaded don't load them again, or if no jars found then load the ones associated with the proxy.

We load our jars manually into custom_jars so the MP has them for sure and to avoid redundant copies in each of the APIs that use them.

Thanks again for the thorough test.

adas
New Member

Let me try and explain how this is intended to work with an example. First of all this feature is not enabled by default, you can enable it by changing the nodejs.properties file on message processors:

jar.loading.allowed=true

Now let's take an example:

imagine that you have packaged a module named "javatest" that resides inside a JAR file called "JavaTest.jar". You write a Node.js script called "testit.js" and place it inside the "resources/node" directory. You would then put the following Node.js code inside "testit.js":

var path = require('path');

// Special built-in module that only exists in Trireme

var trireme = require('trireme');

// Read JAR file from the "resources/node" directory and

// figure out which Node.js modules it implements

var javaModule = trireme.loadJars([path.join(__dirname, 'JavaTest.jar'),path.join(__dirname, 'Library.jar')]);

// This module is implemented in "JavaTest.jar" and this

// will only work after the call to "loadJar".

var javatest = require('javatest');

So in your test, you should be able to load two node apps that use the same Java class, but from different JARs in different resources/node folders and check if they are isolated. I hope this helps.

Not applicable

@arghya das

What you have explained above will work for classloader isolation at nodejs application level. The kind of isolation we are looking for is between APIGEE and Trireme. From within Trireme we are calling downstream applications that are Cassandra based, or in future could really be anything else having jars common to APIGEE. In the current scenario, APIGEE could be on x.x.x version of Cassandra and our downstream application that we are calling from Trireme could be on y.y.y version of Cassandra. If there is no classloader isolation between APIGEE and Trireme, classes loaded initially from APIGEE's Cassandra version will be used while calling Trireme's downstream, which may work sometime and fail at other time depending on if the class definition changed between the versions used by APIGEE and Trireme.

If Trireme gets instantiated with its own classloader isolated from APIGEE, we will be able to add any jars of required versions and any other jars required by the downstream Cassandra calls within Trireme using nodejs loadJars functionality. Is this achievable?

Arghya is correct about how the "trireme-support" module works. JARs loaded from that module have a new classloader that inherits from the root classloader, but they are isolated from each other. That way, you may have many versions of those jars.

However as you point out, the classloader for those jars inherits from the root classloader, so those classes are still able to load stuff that is built in to the message processor.

One way to change this might be to implement your own class loading logic within the jar that loads additional things that it needs. However it's not something that's built in to Trireme or Edge today.

adas
New Member

@ms5838 Thanks for clarifying the expectation. I think there's a gap in the expectation and what was implemented. @greg@apigee.com any thoughts ?