Lucee Classloader not finding all system classes

So I’m running into an issue with using JavaLoader in Lucee 5.3.8.206. The problem is the “loadColdFusionClassPath” option is not finding JARs in the “lucee-server/bundles/” folder.

In troubleshooting this issue, this issue appears to be with differences between how getPageContext().getClass().getClassLoader() and createObject("java") work. It appears getPageContext().getClass().getClassLoader() only has access to some class in Lucee (it appears the jars that start with org.lucee are available).

For example, if you run the following it works:

createObject("java", "org.bouncycastle.mail.smime.util.CRLFOutputStream")

(This comes from bouncycastle.mail-1.38.0.jar.)

However, if you run:

getPageContext().getClass().getClassLoader().loadClass("org.bouncycastle.mail.smime.util.CRLFOutputStream")

It will throw an exception that the class does not exist. (NOTE, I’ve tried using getPageContext().getClassLoader() and that does not work either.)

I think perhaps non-OSGi JARs are not available, because this does work:

getPageContext().getClass().getClassLoader().loadClass("org.apache.http.conn.ClientConnectionManager")

(This comes from org.lucee.httpcomponents.httpclient-4.5.10.jar.)

It appears the issue is that the createObject() funnels calls through lucee.commons.lang.ClassUtil.loadClass() and that’s what handles finding all the classes available to Lucee, because if I do the following, I can load the class:

createObject("java", "lucee.commons.lang.ClassUtil").loadClass(getPageContext().getClass().getClassLoader(), "org.bouncycastle.mail.smime.util.CRLFOutputStream")

Is there a publicly available class Loader in Lucee that funnels calls through the lucee.commons.lang.ClassUtil.loadClass() method?

@dswitzer i’m not sure if i understand your point a 100%. But let me explain what exactly happening here.
The nature of OSGi is NOT to see everything, every OSGi bundle (the Lucee core is a OSGi bundle), defines the relation to other jars.
The Lucee core for example can only see what it defines in "import-package

and in require-bundle

This separation is essential for OSGi, let say “Bundle A” has this “require-bundle”

Require-Bundle: susi-sorglos;bundle-version=1.0.0,

and “Bundle B” has this “require-bundle”

Require-Bundle: susi-sorglos;bundle-version=2.0.0,

OSGi still allows me to use this bundle alongside no problem, even one uses the same bundle in a different version. This works because “B” does not see “A” and “A” does not see “B”.
So this is not a bug, this is what make OSGi work in the first place. Every bundles operates independent of each other.

So when you do this getPageContext().getClass().getClassLoader() you get the classloader from the Lucee core, that CL does not see “org.bouncycastle.mail.smime.util”, because that package is loaded only in the PDF and S3 Extension. So to get this class you need to start with a class from one of this extensions.

like

createObject("java","org.lucee.extension.pdf.function.IsPDFFile").init().loadClass("org.bouncycastle.mail.smime.util.CRLFOutputStream");

So in short, in an OSGI environment a BundleClassloader only does see the classes loaded in his context, it does NOT see classes loaded by other BundleClassloader.

CreateObject is a different story because it does not relay on a single classloader, it looks for a class in all bundleclassloader and more.

There is one classloader in Lucee that sees all classes, the EnvClassloader, this classloader is used for classic jars we convert to OSGi that use internally a simple classloader and expect it to work.
You can get that classloader as following:

getPageContext().getConfig().getClassLoaderEnv();
1 Like

small addition, the EnvClassLoader still applies OSGi Bundle rules. Means it check from where a request for a resource is coming from and if it comes from within a bundle, it limit the request to that bundle. It only allows global access from request not coming from a bundle. (Simplified explanation)

@micstriit

That helps clear things up for me. I think I had my understanding of the issue reversed.

I suppose the better question would be is there a class loader that would emulate the functionality when using createObject('java') (which uses the ClassUtil object).

What I’m trying to do is fix a problem with using the Quartz Scheduler with Lucee (which we use in ACF in our application to manage our own custom scheduled tasks). We’re loading Quartz via Java Loader to resolve some class loader issues. It needs to be able to find the JDBC drivers (specifically in our case com.microsoft.sqlserver.jdbc.SQLServerDriver).

What does work is doing:

createObject("java", "com.microsoft.sqlserver.jdbc.SQLServerDriver");

However, using the following results in class not found issue, because the MSSQL drivers are being loaded via OSGi.

getPageContext().getClass().getClassLoader().loadClass("com.microsoft.sqlserver.jdbc.SQLServerDriver")

I also tried using the getClassLoaderEnv(), but that does not work either:

getPageContext().getConfig().getClassLoaderEnv().loadClass("com.microsoft.sqlserver.jdbc.SQLServerDriver")

I can do:

createObject("java", "com.microsoft.sqlserver.jdbc.SQLServerDriver").getClass().getClassLoader()

To get a reference to a class loader which does allow me to use loadClass() to get the driver, but what I really want is a class loader that will do the same work as lucee.commons.lang.ClassUtil.loadClass().

Other than rolling up my own class loader to use lucee.commons.lang.ClassUtil, is there anything exposed that would work?

As a workaround, what I’ve add expandPath("{lucee-server}../bundles/") to my JAR locations, to include all the bundle files, but I’m thinking that might have some ill side effects and that is not always ideal.