Custom javax.mail.provider throws exception in JSD Email Request

Heiko Mattes August 28, 2020

Hi,

I am a developer for a plugin that integrates with Jira Server and Jira Service Desk. It adds custom mail providers (SMTP/IMAP/POP), to enable encryption/decryption of emails that are sent via Jira.

It does that by adding a javax.mail.Provider file in the META-INF/services folder of a lib that gets installed into the lib folder of a JIRA instance. It declares customer providers, which are loaded at runtime then.

Since Jira 8.10 and its shipping with the new jakarta.mail library, custom providers throw an exception when trying to set up a project specific Email Request. When entering the correct data for a IMPA/POP account, and hitting the update button, it just stops doing anything and puts out an exception in the log file. (see provided stack trace)

I checked, and it seems like the class loader that is passed along to the Session object to get the providers does not 'see' our classes.

It is different for the outgoing IMAP/POP server, that is configured in the System settings. The class loader there finds our classes, and thus does not generate an exception and uses them as well. That is why I also know it is nothing wrong with the configuration in our library.

I hope you can help in that regard. Should you have further questions, don't hesitate to ask.

Regards
Heiko Mattes

error_pic.png

Stacktrace:

[c.a.p.r.c.error.jersey.ThrowableExceptionMapper] Uncaught exception thrown by REST service: javax.mail.Provider: Provider net.savignano.snotify.jira.mailer.provider.SnotifySmtpProvider not found
java.util.ServiceConfigurationError: javax.mail.Provider: Provider net.savignano.snotify.jira.mailer.provider.SnotifySmtpProvider not found
at java.util.ServiceLoader.fail(ServiceLoader.java:239)
at java.util.ServiceLoader.access$300(ServiceLoader.java:185)
at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:372)
at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
at javax.mail.Session.loadProviders(Session.java:964)
at javax.mail.Session.<init>(Session.java:254)
at javax.mail.Session.getInstance(Session.java:281)
at com.atlassian.mail.server.managers.AbstractMailServerManager.getSession(AbstractMailServerManager.java:56)
at com.atlassian.jira.internal.mail.processor.util.MailHelper.getSession(MailHelper.java:48)
at com.atlassian.jira.internal.mail.processor.feature.channel.connectionverifier.DefaultChannelConnectionVerifier.verifyConnectionDefinition(DefaultChannelConnectionVerifier.java:70)
at com.atlassian.jira.internal.mail.processor.feature.channel.connectionverifier.DefaultChannelConnectionVerifier.verifyConnectionDefinition(DefaultChannelConnectionVerifier.java:58)
... 3 filtered
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy3383.verifyConnectionDefinition(Unknown Source)
at com.atlassian.servicedesk.internal.feature.emailchannel.healer.EmailChannelVerificationManager.lambda$testEmailChannel$0(EmailChannelVerificationManager.java:33)
at io.atlassian.fugue.Either$RightProjection.flatMap(Either.java:937)
at io.atlassian.fugue.Either.flatMap(Either.java:231)
at com.atlassian.pocketknife.step.EitherStep1.then(EitherStep1.java:18)
at com.atlassian.servicedesk.internal.feature.emailchannel.healer.EmailChannelVerificationManager.testEmailChannel(EmailChannelVerificationManager.java:33)
at com.atlassian.servicedesk.internal.feature.emailchannel.healer.EmailChannelVerificationService.testEmailChannel(EmailChannelVerificationService.java:40)
at com.atlassian.servicedesk.internal.rest.emailchannel.GlobalEmailChannelResource.testEmailChannel(GlobalEmailChannelResource.java:100)
at com.atlassian.pocketknife.step.EitherStep3.lambda$null$6(EitherStep3.java:38)
at io.atlassian.fugue.Either$RightProjection.map(Either.java:923)
at io.atlassian.fugue.Either.map(Either.java:217)
at com.atlassian.pocketknife.step.EitherStep3.lambda$null$7(EitherStep3.java:38)
at io.atlassian.fugue.Either$RightProjection.flatMap(Either.java:937)
at io.atlassian.fugue.Either.flatMap(Either.java:231)
at com.atlassian.pocketknife.step.EitherStep3.lambda$yield$8(EitherStep3.java:38)
at io.atlassian.fugue.Either$RightProjection.flatMap(Either.java:937)
at io.atlassian.fugue.Either.flatMap(Either.java:231)
at com.atlassian.pocketknife.step.EitherStep3.yield(EitherStep3.java:38)
at com.atlassian.servicedesk.internal.rest.emailchannel.GlobalEmailChannelResource.testEmailServer(GlobalEmailChannelResource.java:72)
... 3 filtered
at java.lang.reflect.Method.invoke(Method.java:498)
... 19 filtered
at com.atlassian.plugins.rest.module.RestDelegatingServletFilter$JerseyOsgiServletContainer.doFilter(RestDelegatingServletFilter.java:160)
... 1 filtered
at com.atlassian.plugins.rest.module.RestDelegatingServletFilter.doFilter(RestDelegatingServletFilter.java:70)
... 32 filtered
at com.atlassian.servicedesk.internal.web.ExternalCustomerLockoutFilter.doFilter(ExternalCustomerLockoutFilter.java:55)
... 4 filtered
at com.atlassian.servicedesk.internal.web.UrlOperationalStatusCheckFilter.doFilterWhenLicensed(UrlOperationalStatusCheckFilter.java:39)
at com.atlassian.servicedesk.internal.web.OperationalStatusAwareHttpFilter.doFilter(OperationalStatusAwareHttpFilter.java:28)
... 4 filtered
at com.atlassian.servicedesk.internal.web.PopulateEyeballForRestFilter.doFilterWhenLicensed(PopulateEyeballForRestFilter.java:36)
at com.atlassian.servicedesk.internal.web.OperationalStatusAwareHttpFilter.doFilter(OperationalStatusAwareHttpFilter.java:28)
... 8 filtered
at com.atlassian.jira.plugin.mobile.web.filter.MobileAppRequestFilter.doFilter(MobileAppRequestFilter.java:59)
... 4 filtered
at com.atlassian.jira.plugin.mobile.login.MobileLoginSuccessFilter.doFilter(MobileLoginSuccessFilter.java:54)
... 3 filtered
at com.atlassian.diagnostics.internal.platform.monitor.http.HttpRequestMonitoringFilter.doFilter(HttpRequestMonitoringFilter.java:55)
... 8 filtered
at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
... 43 filtered
at com.atlassian.ratelimiting.internal.filter.RateLimitFilter.doFilter(RateLimitFilter.java:73)
... 17 filtered
at com.atlassian.jira.security.JiraSecurityFilter.lambda$doFilter$0(JiraSecurityFilter.java:66)
... 1 filtered
at com.atlassian.jira.security.JiraSecurityFilter.doFilter(JiraSecurityFilter.java:64)
... 16 filtered
at com.atlassian.plugins.rest.module.servlet.RestSeraphFilter.doFilter(RestSeraphFilter.java:38)
... 19 filtered
at com.atlassian.jira.servermetrics.CorrelationIdPopulatorFilter.doFilter(CorrelationIdPopulatorFilter.java:30)
... 5 filtered
at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.lambda$invokeFilterChain$0(CustomerContextSettingFilter.java:215)
at com.atlassian.servicedesk.internal.api.util.context.ReentrantThreadLocalBasedCodeContext.rteInvoke(ReentrantThreadLocalBasedCodeContext.java:136)
at com.atlassian.servicedesk.internal.api.util.context.ReentrantThreadLocalBasedCodeContext.runOutOfContext(ReentrantThreadLocalBasedCodeContext.java:89)
at com.atlassian.servicedesk.internal.utils.context.CustomerContextServiceImpl.runOutOfCustomerContext(CustomerContextServiceImpl.java:47)
at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.outOfCustomerContext(CustomerContextSettingFilter.java:206)
at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.doFilterImpl(CustomerContextSettingFilter.java:134)
at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.doFilter(CustomerContextSettingFilter.java:123)
... 4 filtered
at com.atlassian.jwt.internal.servlet.JwtAuthFilter.doFilter(JwtAuthFilter.java:37)
... 8 filtered
at com.atlassian.ratelimiting.internal.filter.RateLimitPreAuthFilter.doFilter(RateLimitPreAuthFilter.java:71)
... 3 filtered
at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
... 4 filtered
at com.atlassian.web.servlet.plugin.LocationCleanerFilter.doFilter(LocationCleanerFilter.java:36)
... 26 filtered
at com.atlassian.jira.servermetrics.MetricsCollectorFilter.doFilter(MetricsCollectorFilter.java:25)
... 25 filtered
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

2 answers

0 votes
Rui Rocha November 20, 2020

Hi,

We are facing the same problem.

We have several automatic events that send emails and as of Jira 8.10, these events have stopped working.
What we managed to find in the logs was the following error, which is identical to yours.

Exception in thread "a3f8539b25b7486295663d6c7da70dd6" java.util.ServiceConfigurationError: javax.mail.Provider: Provider com.sun.mail.imap.IMAPProvider not found

at java.util.ServiceLoader.fail(ServiceLoader.java:239)

at java.util.ServiceLoader.access$300(ServiceLoader.java:185)

at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:372)

at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)

at java.util.ServiceLoader$1.next(ServiceLoader.java:480)

at javax.mail.Session.loadProviders(Session.java:964)

at javax.mail.Session.<init>(Session.java:254)

at javax.mail.Session.getInstance(Session.java:281)

at com.atlassian.mail.server.managers.AbstractMailServerManager.getSession(AbstractMailServerManager.java:56)

at com.atlassian.mail.server.AbstractMailServer.getSessionFromServerManagerInternal(AbstractMailServer.java:445)

at com.atlassian.mail.server.AbstractMailServer.getSessionFromServerManager(AbstractMailServer.java:428)

at com.atlassian.mail.server.impl.SMTPMailServerImpl.getSession(SMTPMailServerImpl.java:157)

at com.atlassian.mail.server.impl.SMTPMailServerImpl.sendWithMessageId(SMTPMailServerImpl.java:180)

at com.atlassian.mail.queue.SingleMailQueueItem.send(SingleMailQueueItem.java:38)

You identified that you got a workaround to solve the problem, can you explain what was done?

Best regards,

Rui Rocha

Heiko Mattes November 23, 2022

Hi,

apparently, I missed your reply. Sorry for that. And even if it is probably coming way too late, it still might help you or someone else who stumbles upon it.

Before we process the email, we check if the current class loader in the thread is the 'correct' one. If so, we just continue with processing. If not, however, we replace the class loader of the current thread with one we know can handle our processing. And once the processing is done, we reset to the previous class loader.

Thread.currentThread().setContextClassLoader(classLoader)

Hope, that still helps.

Regards
Heiko Mattes

Like Metin Savignano likes this
0 votes
Metin Savignano
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 4, 2020

The problem seems to be that the class loader used by the JSD UI in 8.10+ is incompatible with the class loader used in earlier versions. It does not see the provider class. See Known Limitations documentation page for details. Customers please refer to this page for details and a work-around as soon as we find one. May I also recommend to watch that page, so you will be updated of any new findings we may have.

We have filed a bug report with Atlassian, now waiting for a reaction from them.

 

Addendum:

No reaction yet from Atlassian. :-(  However, In the meantime, we've apparently found a work-around from within our app which we released today.

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
SERVER
TAGS
AUG Leaders

Atlassian Community Events