-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add default methods to MailSender
and JavaMailSender
as appropriate
#23651
Conversation
send(..) -methods of JavaMailSenderImpl which is only delegating to other methods are pulled up as default methods in the interfaces JavaMailSender and MailSender, to make these interfaces require less methods to implement.
Express the implementation of JavaMailSenderImpl.send(SimpleMailMessage...) in terms of send(MimeMessage...), and pull it up as a default method of the JavaMailSender interface. This leaves send(MimeMessage...) as the only sending method one is required to implement (in addition to the createMimeMessage methods), as all other send methods eventually ends up invoking this. Extended the implementation of SmartMimeMessage to also be a "bearer" of an optional original message, to be able to keep the semantics of the exceptions thrown from JavaMailSenderImpl.doSend containing the original message instances which failed. The implementation in JavaMailSenderImpl and its associated interfaces now does not pass the original messages as a separate argument to doSend, although this parameter is kept for backwards compatibility, and doSend's behavior is the same as before if the parameter is used.
A class for implementing decorators for JavaMailSender.
9c23a46
to
39724cd
Compare
@runeflobakk thanks for the PR and sorry it took so long to review. I stopped at the bearer thing as it looks quite unrelated to me. If you want to pursue this, we'd need a bit more detail (feel free to create a separate issue). |
send(..) -methods of JavaMailSenderImpl which is only delegating to other methods are pulled up as default methods in the interfaces JavaMailSender and MailSender, to make these interfaces require fewer methods to implement. See gh-23651
MailSender
and JavaMailSender
as appropriate
Thanks for accepting the contribution, @snicoll! I understand that JavaMailSender may not have been a top priority, perhaps especially with the recent large effort to migrate to the jakarta namespace 😅 IMHO, the "bearer thing" is not actually unrelated, but a facility to preserve the information conveyed in the exception handling inside the JavaMailSenderImpl.doSend(MimeMessage[] mimeMessages, Object[] originalMessages) method.
This is maybe not a big issue in practise, and I am fine with leaving it as this. But I included the change to ensure that the existing behavior that you get the same message objects in an exception that you passed to It may be less of a surprise to get the actual message objects you passed in a caught exception, and if you rely on them being the same for correlating with what you tried to send, then this will break if not preserving this behavior. The decorator is a value-add, and not strictly necessary, though I think it demonstrates some of the value of this restructuring/refactoring. You can implement this yourself as a user of Spring, though it would be nice to have such a facility provided by Spring. But again, may as well be a separate issue/PR. |
@runeflobakk It is unrelated in the sense that it does not need to be done for your original proposal to be complete (including with the decorator). As I've said before, we're happy to review this is if you create a separate issue with a small sample that demonstrates the problem. We won't implement it the way you've done it though. |
Absolutely. My intention was only to point out that c3d1a88, as it is now, introduces a minor behavioral change, and I wanted to make sure this was understood and intentional on your part. |
|
Apologize, I missed the detail that So you still need to implement two methods if extending Totally fine with this, of course. |
The mail sending API is structured around the two interfaces
MailSender
andJavaMailSender
, together with the classJavaMailSenderImpl
which implements the two aforementioned interfaces. The numerous overloads ofsend(..)
-methods in the interfaces makes it a bit cumbersome and errorprone to extend these interfaces directly.Default methods introduced with Java 8 can to a large degree resolve this issue by moving from JavaMailSenderImpl to default methods in the interfaces. For methods which is "piping" to another method of the public API, this is a trivial refactoring (a820753). But
JavaMailSenderImpl.send(SimpleMailMessage...)
can not be refactored in such a trivial manner, as this method delegates to the internaldoSend
-method ofJavaMailSenderImpl
. To rectify, this pull-request also contains a suggested change to allow passing SimpleMailMessages contained in SmartMimeMessages further down the method stack, and allow thedoSend
to resolve the original message instances in case of errors, so existing behaviour is preserved. (f95659b)The interfaces are now simpler to extend directly, and this also allows for more clean composable decorators, as demonstrated with the new
JavaMailSenderDecorator
class in 39724cd. It "seals" the default methods asfinal
, allowing decorating an arbitrary JavaMailSender with only overriding one method, which is part of the public API. It is simple to wrap several layers of JavaMailSenderDecorators, if one should need to, and eventually end in a JavaMailSenderImpl.I have used this structure for a client to separately implement a whitelisting
JavaMailSenderDecorator
, and a performance monitoring "aspect"JavaMailSenderDecorator
, which are composed together, and ultimately wrapping an instance ofJavaMailSenderImpl
.This also may yield some advantages when testing, as one would only have to intercept the one
send(..)
-method which is not provided by the interfaces, and tests does not need to know which overload is used by the main code.JavaMailSenderImpl
instances can be separated strictly away from testing. For instance, if using mock libraries as Mockito, mocking aJavaMailSenderDecorator
, it will ensure that all realfinal
methods are invoked, and one may verify invocations of only one method regardless of which overloaded method is invoked by the main code.I hope this can be a relevant contribution, and I am very open for any feedback and discussion on this design!
Thank you :)