Lucee 5 class loading issues with Thread.currentThread().getContextClassLoader().getResourceAsStream()

I’m trying to use the james-jspf project to validate SPF records and am I’m having an issue in Lucee 5.4.6.9 due to the class loader.

I’m using createObject() to create an instance of the JSPF classes and I’m specifying JAR context to use. The very first time I create an instance of the class I need, everything works as expected but any subsequent attempts to create an instance end up throwing an NPE. The only fix is to restart Lucee, which will allow it to work exactly once.

In troubleshooting the issue, the problem is in the org.apache.james.jspf.impl.DefaultTermsFactory class. It’s trying to load a resource file that’s in the JAR using the following:

String termFile = "org/apache/james/jspf/parser/jspf.default.terms";

InputStream is = Thread.currentThread().getContextClassLoader()
        .getResourceAsStream(termFile);

When this code runs the first time, it finds the resource file correctly, but on any additional invocations it can no longer find the org/apache/james/jspf/parser/jspf.default.terms resource.

The problem appears to be due to the use of Thread.currentThread().getContextClassLoader() and that class loader no longer can find the resource. I can get around this issue with any of the following fixes:

  1. I refactor the DefaultTermsFactory to use getClass().getClassLoader() instead of Thread.currentThread().getContextClassLoader(). This makes things work as I’d expect, but that requires either managing my own fork of the code or hoping they accept a pull request.
  2. I can call Thread.currentThread().setContextClassLoader() to temporarily change the class loader before trying to invoke my class and then switch it back once the class has been loaded. I don’t love this approach because it could create some thread safety issues.
  3. I can cache a single instance of the DefaultTermsFactory class in the server scope, so that I only invoke the class once. This is not ideal and could have unintended side effects.

Are there any different techniques I can try? While option #1 is the most ideal, is option #2 going to cause me issues?

Why would Thread.currentThread().getContextClassLoader() be returning different class loaders?

Could it be that the issue is with Thread.currentThread() rather than Thread.currentThread().getContextClassLoader()? After all, Thread.currentThread() returns a reference to the currently executing thread. If the currently executing thread changed, so would Thread.currentThread().