Mail Extension: cfmail fails with "jakarta.mail.Provider: com.sun.mail.imap.IMAPProvider not a subtype" once javax.mail was used first

Versions: Lucee 7.1.0.204, Mail Extension 1.1.0.7 (also 1.1.0.6), Java 21.0.11 (Docker lucee/lucee:7.0-nginx) and 21.0.6 (Tomcat 11, Windows)

Description Since 7.1, cfmail comes from the Mail Extension (jakarta.mail 2.0.2). Lucee still ships the bundle org.lucee.commons-email-all-1.6.0 (javax.mail). Both contain com.sun.mail.imap.IMAPProvider under the same name.

jakarta.mail.Session.loadProviders() uses ServiceLoader with the thread context classloader, which is lucee.runtime.osgi.EnvClassLoader. That loader resolves the class name to whichever variant was requested first. So once any code has created a javax.mail.Session, every cfmail in the JVM fails until restart. It also works the other way round: after cfmail, javax.mail.Session.getInstance() fails with javax.mail.Provider: … not a subtype.

Steps to reproduce (fresh JVM):

<cfset createObject("java", "javax.mail.Session").getInstance(createObject("java", "java.util.Properties").init()) />
<cfmail to="a@b.c" from="a@b.c" subject="x" server="127.0.0.1" port="1" spoolenable="false">x</cfmail>

Expected: connection error. Actual: java.util.ServiceConfigurationError: jakarta.mail.Provider: com.sun.mail.imap.IMAPProvider not a subtype

Stack (excerpt)

at java.util.ServiceLoader.fail
at jakarta.mail.Session.loadProviders(Session.java:964)
at jakarta.mail.Session.getInstance(Session.java:281)
at org.lucee.extension.mail.smtp.SMTPConnectionPool.createSession(SMTPConnectionPool.java:148)
at org.lucee.extension.mail.smtp.SMTPClient.createMimeMessage(SMTPClient.java:492)
at org.lucee.extension.mail.tag.Mail.doEndTag(Mail.java:651)

Suggested fix In SMTPConnectionPool.createSession (and wherever the extension creates a Session or Transport), set the thread context classloader to the extension’s own classloader (e.g. jakarta.mail.Session.class.getClassLoader()) and restore it in finally.

Workaround we applied on the CFML side: create javax.mail.Session and call getStore() with the context classloader set to javax.mail.Session’s classloader. After that, both sides work side by side.

Impact: On our production server no mail was sent for 15 days. A scheduled IMAP check using javax.mail always ran before the first cfmail after a restart.

@schueri89 thanks for the very clear analysis and the suggested fix, that’s exactly what it was.

This is tracked as LDEV-6485. The Mail extension now creates the mail session and transport with its own classloader, so using javax.mail first no longer breaks cfmail.

The fix is in Lucee 7.1.1.15-RC, which bundles Mail extension 1.1.0.10-RC: Lucee 7.1.1.15-RC available for testing

I ran a regression test that uses javax.mail first and then sends with cfmail (request thread, cfthread and the spooler) on 7.1.1.15-RC, and all 5 specs pass. Could you give the RC a try with your scheduled IMAP check, and let us know if cfmail keeps working after it has run?