NoClassDefFound error when using jar instead of class files

Hi all,

Hoping someone can shed some light on this error we’re experiencing since we made a few changes to our application’s build process.

Firstly, the error…

lucee/runtime/type/QueryImpl
java.lang.NoClassDefFoundError

Edit: the stack

  • AdoptOpenJDK JRE 1.8_212_b04 64bit
  • Apache Tomcat 9.0.20
  • Lucee 5.3.3.62

The error mention is occurring when one of our classes attempts to convert a ResultSet to a QueryImpl object, but for some reason now that the packaging has changed, our class can no longer find the internal Lucee classes.

For context, our app has always loaded *.class files from a directory within our application folder. We use an old component called JavaLoader.cfc which I believe is still common today? Previously, we would set a class path to the directory of our classes when initialising JavaLoader.cfc like so…

classPaths = [
		GetDirectoryFromPath(GetCurrentTemplatePath()) & "java/classes"
];
server.javaLoader = createObject("component", "javaloader.JavaLoader").init(loadPaths = classPaths, loadColdFusionClassPath = TRUE);
server.javaLoader = createObject("component", "javaloader.JavaLoader").init(loadColdFusionClassPath = TRUE);

This runs at application startup, and worked well. Recently we decided it was time to package up these class files more nicely, and these class files now reside in WEB-INF/lib within a ‘server.jar’ file. We simplified the above code to the following, in case it’s relevant.

server.javaLoader = createObject("component", "javaloader.JavaLoader").init(loadColdFusionClassPath = TRUE);

No changes to the code have been made other than the above, and the packing of the class files into a jar. Since we’ve made the changes, the application works just fine, except when we attempt to invoke Lucee classes.

Is there a change we need to make to the jar in order for it to see the Lucee runtime classes? Another thread mentioned some manifest changes which made no difference (added “Requires-bundle: lucee.core”).

Would appreciate any ideas you may have!

Cheers,
Glen

Can you describe your stack?

JavaLoader is indeed still in use today for certain use cases, but in yours you may be able to do without it.

To load the classes in your server.jar you can put it anywhere, not necessarily in WEB-INF/lib. Then just specify the jar’s path when instantiating the classes as follows:

myObject = CreateObject( "java", "com.my.class", ExpandPath( "relativePathToJar" ) );

Bear in mind that if any of your classes are already loaded, and they are different versions, you may run into “class clashes” where the wrong version is loaded. Also, once loaded, you won’t be able to update the jar without restarting Lucee.

To avoid these issues you can try converting your jar to an OSGi bundle and then loading that to get the “isolation” and flexibility that JavaLoader also provides.

1 Like

Apologies, I’ve added the stack to my original post.

Julian, the issue is not that the classes within server.jar are not being loaded. They load and run perfectly well, until they invoke Lucee classes (lucee.runtime.type.QueryImpl, in this case) in which case they throw the NoClassDefFoundError exception.

Sure, but does the exception still happen if you load your jar using CreateObject rather than JavaLoader?

You could also try using CreateObject to load the individual class files, the way you were doing before: just specify the directory path as the third argument.

Ah no worries. I’ve made a change such that the class is loaded through the createObject function and it does throw the same error, which leads me to believe the issue isn’t with JavaLoader but something at a higher level. Just a note, the java class that invokes Lucee is also invoked from a CFML file, and within that CFML file I’m able to use the QueryImpl class just fine (which isn’t a huge surprise but thought it worth mentioning)

And yes I could just load the ‘raw’ class files and have it all work again, but I’d much rather have the classes packaged as a jar for tidiness sake. I may need to separate the classes that invoke Lucee and keep them out of the jar if that’s the only course.

Caveat: I’m no java expert - just a cfml dev who makes use of pre-existing jars. Creating java/jars is not something I’ve much experience with.

However, I was able to compile a simple test jar which imported the Lucee core and referenced the lucee.runtime.type.QueryImpl class, and then load and invoke it via CreateObject(). This is with Lucee version 5.3.5.92, the current stable release.

Again, this could be total nonsense due to ignorance, but is it possible your compiled classes no longer have the right references to the lucee.runtime.type package contents? Would re-compiling them reveal this?

That’s ok, I appreciate your efforts nonetheless!

I’ll try with the latest Lucee to confirm if there is a Lucee issue.

In terms of compiling the java classes, I’m compiling them in the exact some way but using an ant ‘jar’ task to package them afterwards. I’ll perform a diff on the files when I get a chance but in theory they should be identical other than being inside a jar.

Hahaha… Good try @Julian_Halliwell :smiley: perhaps you may not be that of a java “ultra” expert, but absolutely a very high ranked cfml dev expert to me!!! You saved my butt more than once :+1:

1 Like

This is resolved! :tada:

Are you able to confirm where and how you loaded your jar when testing @Julian_Halliwell ? I’m still not entirely sure on the why, but I do know what was happening.

The solution ended up being to load the jar directly using JavaLoader exactly as you described, rather than rely on it being on tomcat classpath, like follows…

classPaths = [
	GetDirectoryFromPath(GetCurrentTemplatePath()) & "server.jar"
];
server.javaLoader = createObject("component", "javaloader.JavaLoader").init(loadPaths = classPaths, loadColdFusionClassPath = TRUE);

In trying to solve this, I separated the classes that invoke Lucee and dropped them on the Tomcat classpath (WEB-INF/classes) and they also had the same issue, which is when it clicked.

It does make sense that jars on the Tomcat classpath would be sandboxed from what is happening in Lucee, so I guess I shouldn’t be surprised? I never considered this would be an issue as for simplicities’ sake we leave all of our dependencies and jars on WEB-INF/lib. What is now our ‘server.jar’ was our only exception, and now I know why :slightly_smiling_face:

1 Like

Glad you figured it out.

Actually I didn’t suggest using JavaLoader with a direct path to your jar, but should have done. I’m generally trying to avoid JavaLoader if possible nowadays (it’s old and unsupported), so was suggesting using CreateObject().

That’s how I managed to load my test jar, but it was a bit convoluted.

There is an official guide to using Lucee in Java but as a non-java dev I didn’t find it very clear. It mentions adding the “lucee.jar” to your Java project’s classpath, but I couldn’t find a jar with that name in my Lucee installation. I assumed it meant /lib/lucee-VERSION.jar so added that directory to my classpath.

That didn’t work though: the compiler said the lucee.runtime.type.QueryImpl class was missing. Which it is at that path inside the lucee-VERSION.jar, although it is present in the core package: core\core.lco\lucee\runtime\type\

I then tried adding the tomcat/lucee-server/patches directory to my classpath as this contains the “Lucee core” file which does include the lucee.runtime.type.QueryImpl class with the right path. This worked, but only when I changed the file’s extension from .lco to .jar.

As I say: convoluted. I’m sure someone who understands java properly and the specific way Lucee is built would be able to explain it.