diff --git a/build.gradle b/build.gradle index 2be4c48..a2a4373 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { version = projectVersion group = 'org.grails.plugins' -ext.set('grailsVersion', libs.versions.grails.asProvider().get()) +ext.set('grailsVersion', libs.versions.grails.get()) ext.set('isReleaseVersion', !version.toString().endsWith('-SNAPSHOT')) ext.set('isSnapshot', !isReleaseVersion) @@ -21,43 +21,48 @@ repositories { dependencies { - compileOnly libs.grails.core // Used in public api, but provided - compileOnly libs.groovy.core // Used in public api, but provided - compileOnly libs.servlet.api // Provided + implementation(platform(libs.grails.bom)) + + compileOnly libs.servlet.api // Provided by the servlet container + compileOnly libs.grails.core // Provided as this is a Grails plugin + compileOnly libs.groovy.core // Used in public api, but Groovy is always provided + + compileOnlyApi libs.javamail.api // Used in public api - api libs.javamail.api // Used in public api api libs.spring.context // Used in public api - api libs.spring.context.support // Used in public api + api libs.spring.contextSupport // Used in public api // These two libraries are used in public api, but not in a way // that is meant to be consumed by users of the plugin. // Therefore they are set to implementation to not expose unnecessarily // to the compileClasspath of plugin users. - implementation libs.grails.gsp - implementation libs.grails.web.common + implementation libs.grails.gsp.core + implementation libs.grails.webCommon - implementation libs.grails.web.urlmappings - implementation libs.javamail.impl + implementation libs.grails.gsp.webTaglib + implementation libs.grails.webUrlmappings + implementation libs.javamail.impl // Needs to be implementation (not runtimeOnly), as SMTPMessage is used in MailMessageBuilder implementation libs.spring.beans implementation libs.springboot.autoconfigure - testImplementation libs.grails.testing.support.core + testRuntimeOnly libs.slf4j.simple + testImplementation libs.servlet.api + testImplementation libs.grails.testingSupport.core testImplementation libs.spock.core - integrationTestImplementation libs.groovy.xml integrationTestImplementation libs.greenmail + integrationTestImplementation libs.groovy.xml integrationTestImplementation libs.spring.web - integrationTestRuntimeOnly libs.grails.testing.support.web - integrationTestRuntimeOnly libs.slf4j.nop // Get rid of warning about missing slf4j implementation during test task - integrationTestRuntimeOnly libs.springboot.starter.tomcat + integrationTestRuntimeOnly libs.springboot.starterTomcat + integrationTestRuntimeOnly libs.grails.testingSupport.web } tasks.withType(Test).configureEach { useJUnitPlatform() testLogging { - events 'passed', 'skipped', 'failed' + events 'passed', 'skipped', 'failed', 'standardError', 'standardOut' } } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 61d9189..7e13f52 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -3,6 +3,6 @@ repositories { maven { url = 'https://repo.grails.org/grails/core' } } dependencies { - implementation buildsrcLibs.nexus.publish.gradle.plugin - implementation buildsrcLibs.grails.gradle.plugin + implementation buildsrcLibs.nexusPublishGradlePlugin + implementation buildsrcLibs.grailsGradlePlugin } \ No newline at end of file diff --git a/gradle/buildsrc.libs.versions.toml b/gradle/buildsrc.libs.versions.toml index 0dd107a..dc16d12 100644 --- a/gradle/buildsrc.libs.versions.toml +++ b/gradle/buildsrc.libs.versions.toml @@ -3,5 +3,5 @@ grails-gradle-plugin = '7.0.0-SNAPSHOT' nexus-publish-gradle-plugin = '1.3.0' [libraries] -grails-gradle-plugin = { module = 'org.grails:grails-gradle-plugin', version.ref = 'grails-gradle-plugin' } -nexus-publish-gradle-plugin = { module = 'io.github.gradle-nexus:publish-plugin', version.ref = 'nexus-publish-gradle-plugin' } +grailsGradlePlugin = { module = 'org.grails:grails-gradle-plugin', version.ref = 'grails-gradle-plugin' } +nexusPublishGradlePlugin = { module = 'io.github.gradle-nexus:publish-plugin', version.ref = 'nexus-publish-gradle-plugin' } diff --git a/gradle/documentation-config.gradle b/gradle/documentation-config.gradle index f12f550..439019c 100644 --- a/gradle/documentation-config.gradle +++ b/gradle/documentation-config.gradle @@ -13,7 +13,7 @@ dependencies { add('guideConfiguration', libs.grails.docs) add('guideConfiguration', libs.groovy.templates) - add('guideConfiguration', libs.slf4j.nop) // Get rid of warning about missing slf4j implementation during docs task + add('guideConfiguration', libs.slf4j.nop) // Get rid of logs during guide generation } tasks.withType(Groovydoc).configureEach { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d55d1f..8bc3cac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,36 +1,30 @@ [versions] grails = '7.0.0-SNAPSHOT' -grails-testing-support = '4.0.0-SNAPSHOT' -greenmail = '2.0.1' -groovy = '4.0.23' -gsp = '7.0.0-SNAPSHOT' -javamail = '2.0.1' -servlet-api = '6.0.0' -slf4j = '1.7.36' -spring = '6.1.13' -springboot = '3.3.4' -spock = '2.3-groovy-4.0' +greenmail = '2.1.0' [libraries] -grails-core = { module = 'org.grails:grails-core', version.ref = 'grails' } -grails-gsp = { module = 'org.grails:grails-gsp', version.ref = 'gsp' } -grails-testing-support-core = { module = 'org.grails:grails-testing-support', version.ref = 'grails-testing-support' } -grails-testing-support-web = { module = 'org.grails:grails-web-testing-support', version.ref = 'grails-testing-support' } -grails-web-common = { module = 'org.grails:grails-web-common', version.ref = 'grails' } -grails-web-urlmappings = { module = 'org.grails:grails-web-url-mappings', version.ref = 'grails' } +grails-bom = { module = 'org.grails:grails-bom', version.ref = 'grails' } +grails-core = { module = 'org.grails:grails-core' } greenmail = { module = 'com.icegreen:greenmail', version.ref = 'greenmail' } -groovy-core = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' } -grails-docs = { module = 'org.grails:grails-docs', version.ref = 'grails' } -groovy-templates = { module = 'org.apache.groovy:groovy-templates', version.ref = 'groovy' } -groovy-xml = { module = 'org.apache.groovy:groovy-xml', version.ref = 'groovy' } -servlet-api = { module = 'jakarta.servlet:jakarta.servlet-api', version.ref = 'servlet-api' } -javamail-api = { module = 'jakarta.mail:jakarta.mail-api', version.ref = 'javamail' } -javamail-impl = { module = 'com.sun.mail:jakarta.mail', version.ref = 'javamail' } -slf4j-nop = { module = 'org.slf4j:slf4j-nop', version.ref = 'slf4j' } -spring-beans = { module = 'org.springframework:spring-beans', version.ref = 'spring' } -spring-context = { module = 'org.springframework:spring-context', version.ref = 'spring' } -spring-context-support = { module = 'org.springframework:spring-context-support', version.ref = 'spring' } -spring-web = { module = 'org.springframework:spring-web', version.ref = 'spring' } -springboot-autoconfigure = { module = 'org.springframework.boot:spring-boot-autoconfigure', version.ref = 'springboot' } -springboot-starter-tomcat = { module = 'org.springframework.boot:spring-boot-starter-tomcat', version.ref = 'springboot' } -spock-core = { module = 'org.spockframework:spock-core', version.ref = 'spock' } \ No newline at end of file +grails-docs = { module = 'org.grails:grails-docs' } +grails-gsp-core = { module = 'org.grails:grails-gsp' } +grails-gsp-webTaglib = { module = 'org.grails:grails-web-taglib' } +grails-testingSupport-core = { module = 'org.grails:grails-testing-support' } +grails-testingSupport-web = { module = 'org.grails:grails-web-testing-support' } +grails-webCommon = { module = 'org.grails:grails-web-common' } +grails-webUrlmappings = { module = 'org.grails:grails-web-url-mappings' } +groovy-core = { module = 'org.apache.groovy:groovy' } +groovy-templates = { module = 'org.apache.groovy:groovy-templates' } +groovy-xml = { module = 'org.apache.groovy:groovy-xml' } +servlet-api = { module = 'jakarta.servlet:jakarta.servlet-api' } +javamail-api = { module = 'jakarta.mail:jakarta.mail-api' } +javamail-impl = { module = 'org.eclipse.angus:jakarta.mail' } +slf4j-nop = { module = 'org.slf4j:slf4j-nop' } +slf4j-simple = { module = 'org.slf4j:slf4j-simple' } +spock-core = { module = 'org.spockframework:spock-core' } +spring-beans = { module = 'org.springframework:spring-beans' } +spring-context = { module = 'org.springframework:spring-context' } +spring-contextSupport = { module = 'org.springframework:spring-context-support' } +spring-web = { module = 'org.springframework:spring-web' } +springboot-autoconfigure = { module = 'org.springframework.boot:spring-boot-autoconfigure' } +springboot-starterTomcat = { module = 'org.springframework.boot:spring-boot-starter-tomcat' } diff --git a/src/integration-test/groovy/grails/plugins/mail/MailServiceSpec.groovy b/src/integration-test/groovy/grails/plugins/mail/MailServiceSpec.groovy index 2e91218..c332026 100644 --- a/src/integration-test/groovy/grails/plugins/mail/MailServiceSpec.groovy +++ b/src/integration-test/groovy/grails/plugins/mail/MailServiceSpec.groovy @@ -13,19 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package grails.plugins.mail import com.icegreen.greenmail.util.GreenMail import com.icegreen.greenmail.util.ServerSetupTest import grails.testing.mixin.integration.Integration import groovy.xml.XmlSlurper +import jakarta.inject.Inject +import jakarta.mail.Message +import jakarta.mail.Multipart +import jakarta.mail.internet.MimeBodyPart +import jakarta.mail.internet.MimeMessage +import jakarta.mail.internet.MimeMultipart import org.grails.io.support.ClassPathResource import org.springframework.core.io.FileSystemResource import org.springframework.mail.MailMessage import org.springframework.mail.MailSender import org.springframework.mail.SimpleMailMessage -import org.springframework.mail.javamail.JavaMailSenderImpl +import org.springframework.mail.javamail.JavaMailSender import org.springframework.mail.javamail.MimeMailMessage import org.springframework.mail.javamail.MimeMessageHelper import org.springframework.web.context.request.RequestContextHolder @@ -33,48 +38,46 @@ import spock.lang.Ignore import spock.lang.Shared import spock.lang.Specification -import jakarta.mail.Message -import jakarta.mail.internet.MimeBodyPart -import jakarta.mail.internet.MimeMessage -import jakarta.mail.internet.MimeMultipart - @Integration -class MailServiceSpec extends Specification { +class MailServiceSpec extends Specification { + @Inject + JavaMailSender mimeMailSender MailService mimeCapableMailService MailService nonMimeCapableMailService MailMessageContentRenderer mailMessageContentRenderer - @Shared + @Shared GreenMail greenMail = new GreenMail(ServerSetupTest.SMTP) def setupSpec() { - greenMail.start() + greenMail.start() } def cleanupSpec() { - greenMail.stop() + greenMail.stop() } def setup() { - def mimeMailSender = new JavaMailSenderImpl(host: "localhost", port: ServerSetupTest.SMTP.port) - MailMessageBuilderFactory mimeMessageBuilderFactor = new MailMessageBuilderFactory( - mailSender: mimeMailSender, - mailMessageContentRenderer: mailMessageContentRenderer + def mimeMessageBuilderFactor = new MailMessageBuilderFactory( + mailSender: mimeMailSender, + mailMessageContentRenderer: mailMessageContentRenderer ) mimeCapableMailService = new MailService( - mailMessageBuilderFactory: mimeMessageBuilderFactor, - mailConfigurationProperties: new MailConfigurationProperties()) + mailMessageBuilderFactory: mimeMessageBuilderFactor, + mailConfigurationProperties: new MailConfigurationProperties() + ) mimeCapableMailService.afterPropertiesSet() - MailSender simpleMailSender = new SimpleMailSender() - MailMessageBuilderFactory simpleMessageBuilderFactory = new MailMessageBuilderFactory( - mailSender: simpleMailSender, - mailMessageContentRenderer: mailMessageContentRenderer + def simpleMailSender = new SimpleMailSender() + def simpleMessageBuilderFactory = new MailMessageBuilderFactory( + mailSender: simpleMailSender, + mailMessageContentRenderer: mailMessageContentRenderer ) nonMimeCapableMailService = new MailService( - mailMessageBuilderFactory: simpleMessageBuilderFactory, - mailConfigurationProperties: new MailConfigurationProperties()) + mailMessageBuilderFactory: simpleMessageBuilderFactory, + mailConfigurationProperties: new MailConfigurationProperties() + ) nonMimeCapableMailService.afterPropertiesSet() } @@ -84,463 +87,653 @@ class MailServiceSpec extends Specification { greenMail.reset() } - void testSendSimpleMessage() { - when: - MailMessage message = nonMimeCapableMailService.sendMail { - to "fred@g2one.com" - title "Hello John" - body 'this is some text' - from 'king@g2one.com' - } - then: - message instanceof SimpleMailMessage - "Hello John" == ((SimpleMailMessage)message).getSubject() - 'this is some text' ==message.getText() - 'fred@g2one.com' == message.to[0] - 'king@g2one.com' == message.from - } + void 'should send a simple mail message successfully'() { + given: 'a mail recipient, subject, body, and sender' + String recipient = 'fred@g2one.com' + String subject = 'Hello John' + String bodyText = 'this is some text' + String sender = 'king@g2one.com' - void testAsyncSendSimpleMessage() { - when: - MailMessage message = nonMimeCapableMailService.sendMail { - async true - to "fred@g2one.com" - title "Hello John" - body 'this is some text' - from 'king@g2one.com' + when: 'the mail service sends the message' + def message = nonMimeCapableMailService.sendMail { + to recipient + title subject + body bodyText + from sender } - then: - message instanceof SimpleMailMessage - "Hello John" == ((SimpleMailMessage)message).getSubject() - 'this is some text' == message.getText() - 'fred@g2one.com' == message.to[0] - 'king@g2one.com' == message.from - } - void testSendToMultipleRecipients() { - when: - MailMessage message = nonMimeCapableMailService.sendMail { - to "fred@g2one.com", "ginger@g2one.com" - title "Hello John" - body 'this is some text' + then: 'the message is a SimpleMailMessage and has correct properties' + message instanceof SimpleMailMessage + def smm = (SimpleMailMessage) message + smm.subject == subject + smm.text == bodyText + smm.to[0] == recipient + smm.from == sender + } + + void 'should send a simple mail message asynchronously'() { + given: 'a mail recipient, subject, body, and sender' + String recipient = 'fred@g2one.com' + String subject = 'Hello John' + String bodyText = 'this is some text' + String sender = 'king@g2one.com' + + when: 'the mail service sends the message asynchronously' + def message = nonMimeCapableMailService.sendMail { + async true + to recipient + title subject + body bodyText + from sender } - then: - message.subject == "Hello John" - message.text == "this is some text" - message.to[0] == "fred@g2one.com" - message.to[1] == "ginger@g2one.com" - } + then: 'the message is a SimpleMailMessage and has the correct properties' + message instanceof SimpleMailMessage + def smm = (SimpleMailMessage) message + smm.subject == subject + smm.text == bodyText + smm.to[0] == recipient + smm.from == sender + } + + void 'should send a mail message to multiple recipients'() { + given: 'multiple mail recipients and a subject and body' + String recipient1 = 'fred@g2one.com' + String recipient2 = 'ginger@g2one.com' + String subject = 'Hello John' + String bodyText = 'this is some text' + + when: 'the mail service sends the message to multiple recipients' + def message = (SimpleMailMessage) nonMimeCapableMailService.sendMail { + to recipient1, recipient2 + title subject + body bodyText + } + then: 'the message has the correct subject, body, and recipients' + message.subject == subject + message.text == bodyText + message.to.size() == 2 + message.to[0] == recipient1 + message.to[1] == recipient2 + } + void 'should send a mail message to multiple recipients using a list'() { + given: 'a list of recipients, subject, and body for the mail message' + List recipients = ['fred@g2one.com', 'ginger@g2one.com'] + String subject = 'Hello John' + String bodyText = 'this is some text' - void testSendToMultipleRecipientsUsingList() { - when: - MailMessage message = nonMimeCapableMailService.sendMail { - to(["fred@g2one.com", "ginger@g2one.com"]) - title "Hello John" - body 'this is some text' + when: 'the mail service sends the message to the recipients list' + def message = (SimpleMailMessage) nonMimeCapableMailService.sendMail { + to recipients + title subject + body bodyText } - then: - message.getTo()[0] == "fred@g2one.com" - message.getTo()[1] == "ginger@g2one.com" - } - void testSendToMultipleCCRecipientsUsingList() { - when: - MailMessage message = nonMimeCapableMailService.sendMail { - to 'joe@g2one.com' - cc(["fred@g2one.com", "ginger@g2one.com"]) - title "Hello John" - body 'this is some text' + then: 'the message should have the correct recipients' + message.to.size() == 2 + message.to[0] == recipients[0] + message.to[1] == recipients[1] + } + + void 'should send a mail message with multiple CC recipients using a list'() { + given: 'a recipient, CC recipients list, subject, and body for the mail message' + String recipient = 'joe@g2one.com' + List ccRecipients = ['fred@g2one.com', 'ginger@g2one.com'] + String subject = 'Hello John' + String bodyText = 'this is some text' + + when: 'the mail service sends the message with CC recipients' + def message = (SimpleMailMessage) nonMimeCapableMailService.sendMail { + to recipient + cc ccRecipients + title subject + body bodyText } - then: - "fred@g2one.com" == message.cc[0] - "ginger@g2one.com" == message.cc[1] - } - void testSendToMultipleBCCRecipientsUsingList() { - when: - MailMessage message = nonMimeCapableMailService.sendMail { - to("joe@g2one.com") - bcc(["fred@g2one.com", "ginger@g2one.com"]) - title "Hello John" - body 'this is some text' + then: 'the message has the correct main recipient and CC recipients' + message.to[0] == recipient + message.cc.size() == 2 + message.cc[0] == ccRecipients[0] + message.cc[1] == ccRecipients[1] + } + + void 'should send a mail message with multiple BCC recipients using a list'() { + given: 'a recipient, BCC recipients list, subject, and body for the mail message' + String recipient = 'joe@g2one.com' + List bccRecipients = ['fred@g2one.com', 'ginger@g2one.com'] + String subject = 'Hello John' + String bodyText = 'this is some text' + + when: 'the mail service sends the message with BCC recipients' + def message = (SimpleMailMessage) nonMimeCapableMailService.sendMail { + to recipient + bcc bccRecipients + title subject + body bodyText } - then: - "fred@g2one.com" == message.bcc[0] - "ginger@g2one.com" == message.bcc[1] - } - void testSendToMultipleRecipientsAndCC() { - when: - MailMessage message = nonMimeCapableMailService.sendMail { - to "fred@g2one.com", "ginger@g2one.com" - from "john@g2one.com" - cc "marge@g2one.com", "ed@g2one.com" - bcc "joe@g2one.com" - title "Hello John" - body 'this is some text' + then: 'the message has the correct main recipient and BCC recipients' + message.to[0] == recipient + message.bcc.size() == 2 + message.bcc[0] == bccRecipients[0] + message.bcc[1] == bccRecipients[1] + } + + void 'should send a mail message to multiple recipients with CC and BCC'() { + given: 'a list of recipients, CC recipients, BCC recipients, subject, body, and sender' + List toRecipients = ['fred@g2one.com', 'ginger@g2one.com'] + List ccRecipients = ['marge@g2one.com', 'ed@g2one.com'] + List bccRecipients = ['joe@g2one.com'] + String sender = 'john@g2one.com' + String subject = 'Hello John' + String bodyText = 'this is some text' + + when: 'the mail service sends the message with recipients, CC, BCC' + def message = (SimpleMailMessage) nonMimeCapableMailService.sendMail { + to toRecipients + from sender + cc ccRecipients + bcc bccRecipients + title subject + body bodyText } - then: - message instanceof SimpleMailMessage - "Hello John" == ((SimpleMailMessage)message).getSubject() - message.text == 'this is some text' - message.to.size() == 2 - message.getTo()[0] == "fred@g2one.com" - message.getTo()[1] == "ginger@g2one.com" - message.from == "john@g2one.com" - message.getText() == 'this is some text' - message.to.size() == 2 - message.getTo()[0] == "fred@g2one.com" - message.getTo()[1] == "ginger@g2one.com" - message.from == "john@g2one.com" - message.cc.size() == 2 - message.cc[0] == "marge@g2one.com" - message.cc[1] == "ed@g2one.com" - } - - void testSendMailWithEnvelopeFrom() { - given: - def message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - title "Hello John" - body 'this is some text' - from 'king@g2one.com' - envelopeFrom 'peter@g2one.com' + then: 'the message is a SimpleMailMessage with correct recipients, CC, BCC, and content' + message.subject == subject + message.text == bodyText + message.from == sender + + and: 'the to recipients are correct' + message.to.length == toRecipients.size() + message.to.toList() == toRecipients + + and: 'the cc recipients are correct' + message.cc.length == ccRecipients.size() + message.cc.toList() == ccRecipients + + and: 'the bcc recipients are correct' + message.bcc.length == bccRecipients.size() + message.bcc.toList() == bccRecipients + } + + void 'should send mail with envelopeFrom correctly set'() { + given: 'a mail message with a recipient, subject, body, from, and envelopeFrom' + String recipient = 'fred@g2one.com' + String sender = 'king@g2one.com' + String envelopeSender = 'peter@g2one.com' + String subject = 'Hello John' + String bodyText = 'this is some text' + + when: 'the mail service sends the message with envelopeFrom' + def message = (MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + title subject + body bodyText + from sender + envelopeFrom envelopeSender } - when: - def msg = message.mimeMessage - def greenMsg = greenMail.getReceivedMessages()[0] - then: - msg.getSubject() == "Hello John" - msg.getFrom()[0].toString() == "king@g2one.com" - greenMsg.getHeader("Return-Path")[0] == "" - } - void testSendMailWithEnvelopeFromAndBasicMailSender() { - when: - nonMimeCapableMailService.sendMail { - to "fred@g2one.com" - title "Hello John" - body 'this is some text' - from 'king@g2one.com' - envelopeFrom 'peter@g2one.com' - } - then: - thrown GrailsMailException + and: 'the mime message and received message are retrieved' + def msg = message.mimeMessage + def greenMsg = greenMail.receivedMessages[0] + + then: 'the mime message should have the correct subject and sender' + msg.subject == subject + msg.from[0].toString() == sender + + and: 'the received message should have the correct envelopeFrom (Return-Path)' + greenMsg.getHeader('Return-Path')[0] == "<$envelopeSender>" + } + + void 'should throw GrailsMailException when using envelopeFrom with basic mail sender'() { + given: 'a mail message with a recipient, subject, body, from, and envelopeFrom' + String recipient = 'fred@g2one.com' + String sender = 'king@g2one.com' + String envelopeSender = 'peter@g2one.com' + String subject = 'Hello John' + String bodyText = 'this is some text' + + when: 'the mail service sends the message with envelopeFrom' + nonMimeCapableMailService.sendMail { + to recipient + title subject + body bodyText + from sender + envelopeFrom envelopeSender + } + then: 'a GrailsMailException is thrown' + thrown GrailsMailException } - void testSendHtmlMail() { - when: - MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" - html 'Hello World' + void 'should send an HTML mail message successfully'() { + given: 'a recipient and an HTML message content' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello John' + String htmlContent = 'Hello World' + + when: 'the mail service sends the HTML message' + def message = (MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject + html htmlContent } - then: - message.getMimeMessage().getSubject() == "Hello John" + + then: 'the message should have the correct subject, content type, and content' + message.mimeMessage.subject == mailSubject message.mimeMessage.contentType.startsWith('text/html') - message.getMimeMessage().getContent() == 'Hello World' + message.mimeMessage.content == htmlContent } - void testSendMailView() { - when: - MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" - body(view: '/_testemails/test', model: [msg: 'hello']) - } - then: - message.getMimeMessage().getSubject() == "Hello John" - message.mimeMessage.contentType.startsWith('text/plain') - message.getMimeMessage().getContent().trim() == 'Message is: hello' - } + void 'should send a mail message using a view successfully'() { + given: 'a recipient, subject, and body content using a view with a model' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello John' + Map model = [msg: 'hello'] + String expectedContent = 'Message is: hello' - void testSendMailViewText() { - when: - MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" - text view: '/_testemails/test', model: [msg: 'hello'] + when: 'the mail service sends the message using a view' + def message = (MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject + body(view: '/_testemails/test', model: model) } - then: - message.mimeMessage.contentType.startsWith('text/plain') - message.getMimeMessage().getContent().trim() == 'Message is: hello' - } - void testSendMailViewHtmlMethod() { - when: - MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" - html view: '/_testemails/testhtml', model: [msg: 'hello'] - } - then: - message.getMimeMessage().getSubject() == "Hello John" - message.mimeMessage.contentType.startsWith('text/html') - message.getMimeMessage().getContent().trim() == 'Message is: hello' + then: 'the message should have the correct subject, content type, and content' + message.mimeMessage.subject == mailSubject + message.mimeMessage.contentType.startsWith('text/plain') + message.mimeMessage.content.toString().trim() == expectedContent } - void testSendMailViewHTML() { - when: - MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" - body(view: '/_testemails/testhtml', model: [msg: 'hello']) + void 'should send a text mail message using a view successfully'() { + given: 'a recipient, subject, and text content using a view with a model' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello John' + Map model = [msg: 'hello'] + String expectedContent = 'Message is: hello' + + when: 'the mail service sends the text mail message using a view' + def message = (MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject + text(view: '/_testemails/test', model: model) } - then: - message.getMimeMessage().getSubject() == "Hello John" - message.mimeMessage.contentType.startsWith('text/html') - message.getMimeMessage().getContent().trim() == 'Message is: hello' - } - void testSendMailViewWithTags() { - when: - MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" + then: 'the message should have the correct content type and content' + message.mimeMessage.contentType.startsWith('text/plain') + message.mimeMessage.content.toString().trim() == expectedContent + } + + void 'should send an HTML mail message using a view successfully'() { + given: 'a recipient, subject, and HTML content using a view with a model' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello John' + Map model = [msg: 'hello'] + String expectedContent = 'Message is: hello' + + when: 'the mail service sends the HTML message using a view' + def message = ((MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject + html(view: '/_testemails/testhtml', model: model) + }).mimeMessage + + then: 'the message should have the correct subject, content type, and content' + message.subject == mailSubject + message.contentType.startsWith('text/html') + message.content.toString().trim() == expectedContent + } + + void 'should send a mail message using an HTML view successfully'() { + given: 'a recipient, subject, and HTML content using a view with a model' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello John' + Map model = [msg: 'hello'] + String expectedContent = 'Message is: hello' + + when: 'the mail service sends the message using an HTML view' + def message = ((MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject + body(view: '/_testemails/testhtml', model: model) + }).mimeMessage + + then: 'the message should have the correct subject, content type, and content' + message.subject == mailSubject + message.contentType.startsWith('text/html') + message.content.toString().trim() == expectedContent + } + + void 'should send mail messages using a view with tags'() { + given: 'a recipient and subject for the mail messages' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello John' + + when: 'the mail service sends messages with different conditions' + def messageTrue = ((MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject body(view: '/_testemails/tagtest', model: [condition: true]) - } - MimeMailMessage message2 = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" + }).mimeMessage + def messageFalse = ((MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject body(view: '/_testemails/tagtest', model: [condition: false]) - } - then: + }).mimeMessage - message.getMimeMessage().getSubject() == "Hello John" - message.getMimeMessage().getContent().trim() == 'Condition is true' - message2.getMimeMessage().getSubject() == "Hello John" - message2.getMimeMessage().getContentType()?.startsWith('text/plain') - message2.getMimeMessage().getContent().trim() == '' + then: 'the first message should reflect the true condition' + messageTrue.subject == mailSubject + messageTrue.content.toString().trim() == 'Condition is true' + + and: 'the second message should reflect the false condition' + messageFalse.subject == mailSubject + messageFalse.contentType.startsWith('text/plain') + messageFalse.content.toString().trim() == '' } - void testSendMailViewNoModel() { - when: - MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" + void 'should send a mail message using a view without a model successfully'() { + given: 'a recipient and subject for the mail message' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello John' + + when: 'the mail service sends the message using a view without a model' + def message = ((MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject body(view: '/_testemails/test') - } - then: - message.getMimeMessage().getSubject() == "Hello John" - message.getMimeMessage().getContentType()?.startsWith('text/plain') - message.getMimeMessage().getContent().trim() == 'Message is:' + }).mimeMessage + + then: 'the message should have the correct subject and content type' + message.subject == mailSubject + message.contentType.startsWith('text/plain') + + and: 'the content of the message should be as expected' + message.content.toString().trim() == 'Message is:' } /** - * Tests the "headers" feature of the mail DSL. It should add the + * Tests the 'headers' feature of the mail DSL. It should add the * specified headers to the underlying MIME message. */ - void testSendMailWithHeaders() { - when: - MailMessage message = mimeCapableMailService.sendMail { - headers "X-Mailing-List": "user@grails.codehaus.org", - "Sender": "dilbert@somewhere.org" - to "fred@g2one.com" - subject "Hello Fred" - body 'How are you?' + void 'should send a mail message with custom headers successfully'() { + given: 'a recipient, subject, body, and custom headers for the mail message' + String recipient = 'fred@g2one.com' + String mailSubject = 'Hello Fred' + String bodyText = 'How are you?' + Map customHeaders = [ + 'X-Mailing-List': 'user@grails.codehaus.org', + 'Sender' : 'dilbert@somewhere.org' + ] + + when: 'the mail service sends the message with custom headers' + def message = mimeCapableMailService.sendMail { + headers customHeaders + to recipient + subject mailSubject + body bodyText } - MimeMessage msg = ((MimeMailMessage)message).mimeMessageHelper.mimeMessage - then: + + then: 'the message should be of type MimeMailMessage' message instanceof MimeMailMessage + def msg = ((MimeMailMessage) message).mimeMessage + + and: 'the message should have the correct headers' + msg.getHeader('X-Mailing-List')[0] == 'user@grails.codehaus.org' + msg.getHeader('Sender')[0] == 'dilbert@somewhere.org' - msg.getHeader("X-Mailing-List")[0] == "user@grails.codehaus.org" - msg.getHeader("Sender")[0] == "dilbert@somewhere.org" - to(msg) == ["fred@g2one.com"] - msg.subject == "Hello Fred" - msg.content == "How are you?" + and: 'the message should have the correct recipient, subject, and content' + msg.getRecipients(Message.RecipientType.TO)*.toString() == [recipient] + msg.subject == mailSubject + msg.content == bodyText } /** * Tests that the builder throws an exception if the user tries to * specify custom headers with just a plain MailSender. */ - void testSendMailWithHeadersAndBasicMailSender() { - when: - nonMimeCapableMailService.sendMail { - headers "Content-Type": "text/plain;charset=UTF-8", - "Sender": "dilbert@somewhere.org" - to "fred@g2one.com" - subject "Hello Fred" - body 'How are you?' - } - then: - thrown GrailsMailException + void 'should throw GrailsMailException when sending mail with headers using non-MIME capable mail sender'() { + given: 'custom headers, a recipient, subject, and body for the mail message' + Map mailHeaders = [ + 'Content-Type': 'text/plain;charset=UTF-8', + 'Sender' : 'dilbert@somewhere.org' + ] + String recipient = 'fred@g2one.com' + String subject = 'Hello Fred' + String bodyText = 'How are you?' + + when: 'the non-MIME capable mail service attempts to send the message with headers' + nonMimeCapableMailService.sendMail { + headers mailHeaders + to recipient + subject subject + body bodyText + } + + then: 'a GrailsMailException should be thrown' + thrown GrailsMailException } - void testSendmailWithTranslations() { - when: - MailMessage message = mimeCapableMailService.sendMail { - from 'neur0maner@gmail.com' - to "neur0maner@gmail.com" + void 'should send mail messages with translations based on locale'() { + given: 'a sender, recipient, subject, and body view for both English and French locales' + String sender = 'neur0maner@gmail.com' + String recipient = 'neur0maner@gmail.com' + String mailSubject = 'Hello' + Map model = [name: 'Luis'] + + when: 'the mail service sends a message with English locale' + def messageEn = mimeCapableMailService.sendMail { + from sender + to recipient locale 'en_US' - subject "Hello" - body(view: '/_testemails/i18ntest', model: [name: 'Luis']) + subject mailSubject + body(view: '/_testemails/i18ntest', model: model) } - MailMessage message2 = mimeCapableMailService.sendMail { - from 'neur0maner@gmail.com' - to "neur0maner@gmail.com" + + and: 'the mail service sends a message with French locale' + def messageFr = mimeCapableMailService.sendMail { + from sender + to recipient locale Locale.FRENCH - subject "Hello" - body(view: '/_testemails/i18ntest', model: [name: 'Luis']) + subject mailSubject + body(view: '/_testemails/i18ntest', model: model) } - MimeMessage msg = ((MimeMailMessage)message).mimeMessageHelper.mimeMessage - MimeMessage msg2 = ((MimeMailMessage)message2).mimeMessageHelper.mimeMessage - final def slurper = new XmlSlurper() - def html = slurper.parseText(msg.content) - def html2 = slurper.parseText(msg2.content) - then: - html.body.toString() == 'Translate this: Luis' - html2.body.toString() == 'Traduis ceci: Luis' - } - void testSendmailWithByteArrayAttachment() { - when: - MailMessage message = mimeCapableMailService.sendMail { + then: 'the messages should be of type MimeMailMessage' + messageEn instanceof MimeMailMessage + messageFr instanceof MimeMailMessage + + and: 'the content should be translated correctly for both locales' + MimeMessage msgEn = ((MimeMailMessage) messageEn).mimeMessage + MimeMessage msgFr = ((MimeMailMessage) messageFr).mimeMessage + + def slurper = new XmlSlurper() + def htmlEn = slurper.parseText(msgEn.content.toString()) + def htmlFr = slurper.parseText(msgFr.content.toString()) + + htmlEn.body.toString() == 'Translate this: Luis' + htmlFr.body.toString() == 'Traduis ceci: Luis' + } + + void 'should send an email with a byte array attachment'() { + given: 'the email details and byte array attachment' + String recipient1 = 'fred@g2one.com' + String recipient2 = 'ginger@g2one.com' + String sender = 'john@g2one.com' + String ccRecipient1 = 'marge@g2one.com' + String ccRecipient2 = 'ed@g2one.com' + String bccRecipient = 'joe@g2one.com' + String subject = 'Hello John' + String attachmentName = 'fileName' + String attachmentContentType = 'text/plain' + byte[] attachmentBytes = 'Hello World'.getBytes('US-ASCII') + String htmlBody = 'this is some text' + + when: 'the mail service sends the email with a byte array attachment' + def message = (MimeMailMessage) mimeCapableMailService.sendMail { multipart true - - to "fred@g2one.com", "ginger@g2one.com" - from "john@g2one.com" - cc "marge@g2one.com", "ed@g2one.com" - bcc "joe@g2one.com" - title "Hello John" - attachBytes 'fileName', 'text/plain', 'Hello World'.getBytes("US-ASCII") - html 'this is some text' + to recipient1, recipient2 + from sender + cc ccRecipient1, ccRecipient2 + bcc bccRecipient + title subject + attach attachmentName, attachmentContentType, attachmentBytes + html htmlBody } - def content = ((MimeMailMessage)message).mimeMessage.content - then: + def content = (Multipart) message.mimeMessage.content + + then: 'the email should contain the correct number of parts and attachment content' content.count == 2 content.getBodyPart(1).inputStream.text == 'Hello World' } - void testSendmailWithByteArrayAndResourceAttachments() { - given: - File tmpFile - when: - MailMessage message = mimeCapableMailService.sendMail { + void 'should send an email with byte array and resource attachments'() { + given: 'temporary file for resource attachment and email details' + File tmpFile = null + String recipient1 = 'fred@g2one.com' + String recipient2 = 'ginger@g2one.com' + String sender = 'john@g2one.com' + String ccRecipient1 = 'marge@g2one.com' + String ccRecipient2 = 'ed@g2one.com' + String bccRecipient = 'joe@g2one.com' + String subject = 'Hello John' + String byteArrayAttachmentName = 'fileName' + String byteArrayContentType = 'text/plain' + byte[] byteArrayAttachment = 'Dear John'.getBytes('US-ASCII') + String resourceAttachmentName = 'fileName2' + String resourceContentType = 'application/octet-stream' + String htmlBody = 'this is some text' + + when: 'the mail service sends the email with byte array and resource attachments' + MailMessage message = (MimeMailMessage) mimeCapableMailService.sendMail { multipart true - tmpFile = File.createTempFile('testSendmailWithAttachments',null) + tmpFile = File.createTempFile('testSendmailWithAttachments', null) tmpFile << 'Hello World' - to "fred@g2one.com", "ginger@g2one.com" - from "john@g2one.com" - cc "marge@g2one.com", "ed@g2one.com" - bcc "joe@g2one.com" - title "Hello John" - attach 'fileName', 'text/plain', 'Dear John'.getBytes("US-ASCII") - attach 'fileName2', 'application/octet-stream', new FileSystemResource(tmpFile) - html 'this is some text' + to recipient1, recipient2 + from sender + cc ccRecipient1, ccRecipient2 + bcc bccRecipient + title subject + attach byteArrayAttachmentName, byteArrayContentType, byteArrayAttachment + attach resourceAttachmentName, resourceContentType, new FileSystemResource(tmpFile) + html htmlBody } - def content=message.mimeMessage.content - then: + + def content = (Multipart) message.mimeMessage.content + + then: 'the email should contain the correct number of parts and attachment contents' content.count == 3 content.getBodyPart(1).inputStream.text == 'Dear John' content.getBodyPart(2).inputStream.text == 'Hello World' - cleanup: + + cleanup: 'delete the temporary file' tmpFile?.delete() } - void testInlineAttachment() { - when: - + void 'should send an email with an inline attachment'() { + given: 'the byte array of the inline attachment and email details' byte[] bytes = new ClassPathResource('assets/grailslogo.png').inputStream.bytes - - MailMessage message = mimeCapableMailService.sendMail { + String recipient1 = 'fred@g2one.com' + String recipient2 = 'ginger@g2one.com' + String sender = 'john@g2one.com' + String subject = 'Hello John' + String bodyText = 'this is some text ' + String inlineContentId = 'abc123' + String inlineContentType = 'image/png' + + when: 'the mail service sends the email with an inline attachment' + def message = (MimeMailMessage) mimeCapableMailService.sendMail { multipart true - to "fred@g2one.com", "ginger@g2one.com" - from "john@g2one.com" - title "Hello John" - text 'this is some text ' - inline 'abc123', 'image/png', bytes + to recipient1, recipient2 + from sender + title subject + text bodyText + inline inlineContentId, inlineContentType, bytes } - def inlinePart = ((MimeMailMessage)message).mimeMessage.content.getBodyPart(0).content.getBodyPart("") - then: + + def inlinePart = ((MimeMultipart) ((MimeBodyPart) ((Multipart) message.mimeMessage.content).getBodyPart(0)).content).getBodyPart('') + + then: 'the inline attachment should match the original bytes' inlinePart.inputStream.bytes == bytes } - void testHtmlContentType() { - when: - MimeMessage msg = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "test" - html 'How are you?' - }.mimeMessage - then: - msg.contentType.startsWith("text/html") - msg.content == 'How are you?' - } + void 'should send an email with HTML content type'() { + given: 'email details' + String recipient = 'fred@g2one.com' + String mailSubject = 'test' + String htmlContent = 'How are you?' - void testMultipart_html_first() { - when: - MimeMessage msg = mimeCapableMailService.sendMail { - multipart true - to "fred@g2one.com" - subject "test" - html 'How are you?' - text 'How are you?' - }.mimeMessage - MimeMultipart mp = msg.content.getBodyPart(0).content.getBodyPart(0).content - then: - mp.count == 2 - mp.getBodyPart(0).contentType.startsWith('text/plain') - mp.getBodyPart(0).content == 'How are you?' - mp.getBodyPart(1).contentType.startsWith('text/html') - mp.getBodyPart(1).content == 'How are you?' + when: 'the mail service sends the email with HTML content' + def msg = ((MimeMailMessage) mimeCapableMailService.sendMail { + to recipient + subject mailSubject + html htmlContent + }).mimeMessage + + then: 'the email should have the correct content type and content' + msg.contentType.startsWith('text/html') + msg.content == htmlContent } - void testMultipart_text_first() { - when: - MimeMessage msg = mimeCapableMailService.sendMail { + void 'should send a multipart email with text content first and HTML content last'() { + given: 'email details with multipart configuration' + String recipient = 'fred@g2one.com' + String mailSubject = 'test' + String htmlContent = 'How are you?' + String textContent = 'How are you?' + + when: 'the mail service sends the email with HTML and text content' + def msg = (MimeMailMessage) mimeCapableMailService.sendMail { multipart true - to "fred@g2one.com" - subject "test" - text 'How are you?' - html 'How are you?' - }.mimeMessage - MimeMultipart mp = msg.content.getBodyPart(0).content.getBodyPart(0).content - then: + to recipient + subject mailSubject + html htmlContent + text textContent + } + def mp = (MimeMultipart) ((MimeMultipart) ((MimeMultipart) msg.mimeMessage.content).getBodyPart(0).content).getBodyPart(0).content + + then: 'the email should contain the correct number of parts and content types' mp.count == 2 mp.getBodyPart(0).contentType.startsWith('text/plain') - mp.getBodyPart(0).content == 'How are you?' + mp.getBodyPart(0).content == textContent mp.getBodyPart(1).contentType.startsWith('text/html') - mp.getBodyPart(1).content == 'How are you?' + mp.getBodyPart(1).content == htmlContent } - void testMultipartMode() { - when: - MimeMessage msg = mimeCapableMailService.sendMail { + void 'should send an email in MULTIPART_MODE_RELATED mode'() { + given: 'email details with multipart mode related' + String recipient = 'fred@g2one.com' + String mailSubject = 'test' + String textContent = 'How are you?' + String htmlContent = 'How are you?' + + when: 'the mail service sends the email with related multipart mode' + MimeMessage msg = ((MimeMailMessage) mimeCapableMailService.sendMail { multipart MimeMessageHelper.MULTIPART_MODE_RELATED - to "fred@g2one.com" - subject "test" - text 'How are you?' - html 'How are you?' - }.mimeMessage + to recipient + subject mailSubject + text textContent + html htmlContent + }).mimeMessage - then: + then: 'the email content should be a MimeMultipart instance' msg.content instanceof MimeMultipart - and: - MimeMultipart content = (MimeMultipart) msg.content - + and: 'the content should contain a body part' + def content = (MimeMultipart) msg.content content.getBodyPart(0) instanceof MimeBodyPart - and: - MimeBodyPart mimeBodyPart = content.getBodyPart(0) - MimeMultipart mp = mimeBodyPart.content - + and: 'the multipart body part should contain the expected text and HTML parts' + def mimeBodyPart = (MimeBodyPart) content.getBodyPart(0) + def mp = (MimeMultipart) mimeBodyPart.content mp.count == 2 - mp.getBodyPart(0) instanceof MimeBodyPart - ((MimeBodyPart) mp.getBodyPart(0)).contentType.startsWith('text/plain') - ((MimeBodyPart) mp.getBodyPart(0)).content == 'How are you?' - - mp.getBodyPart(1) instanceof MimeBodyPart - ((MimeBodyPart) mp.getBodyPart(1)).contentType.startsWith('text/html') - ((MimeBodyPart) mp.getBodyPart(1)).content == 'How are you?' + mp.getBodyPart(0).contentType.startsWith('text/plain') + mp.getBodyPart(0).content == textContent + mp.getBodyPart(1).contentType.startsWith('text/html') + mp.getBodyPart(1).content == htmlContent } @Ignore('Not possible to get the currentResponse') @@ -549,12 +742,12 @@ class MailServiceSpec extends Specification { String originalContentType = RequestContextHolder.currentRequestAttributes().currentResponse.contentType MimeMailMessage message = mimeCapableMailService.sendMail { - to "fred@g2one.com" - subject "Hello John" + to 'fred@g2one.com' + subject 'Hello John' body(view: '/test', model: [msg: 'hello']) } then: - message.getMimeMessage().getSubject() == "Hello John" + message.getMimeMessage().getSubject() == 'Hello John' message.mimeMessage.contentType.startsWith('text/plain') == true message.getMimeMessage().getContent().trim() == 'Message is: hello' RequestContextHolder.currentRequestAttributes().currentResponse.contentType == originalContentType @@ -562,29 +755,18 @@ class MailServiceSpec extends Specification { // void testViewResolutionFromPlugin() { // MimeMailMessage message = mimeCapableMailService.sendMail { -// to "fred@g2one.com" -// subject "Hello John" +// to 'fred@g2one.com' +// subject 'Hello John' // body view: '/email/email', plugin: 'for-plugin-view-resolution' // } // -// message.getMimeMessage().getSubject() == "Hello John" +// message.getMimeMessage().getSubject() == 'Hello John' // message.mimeMessage.contentType.startsWith('text/plain') == true // assertEquals 'This is from a plugin!!!', message.getMimeMessage().getContent().trim() // } - - private List to(MimeMessage msg) { - msg.getRecipients(Message.RecipientType.TO)*.toString() - } - - private List cc(MimeMessage msg) { - msg.getRecipients(Message.RecipientType.CC)*.toString() - } - - private List bcc(MimeMessage msg) { - msg.getRecipients(Message.RecipientType.BCC)*.toString() - } } -class SimpleMailSender implements MailSender { + +class SimpleMailSender implements MailSender { void send(SimpleMailMessage simpleMessage) {} void send(SimpleMailMessage[] simpleMessages) {} } diff --git a/src/integration-test/resources/application-test.yml b/src/integration-test/resources/application-test.yml new file mode 100644 index 0000000..272df0e --- /dev/null +++ b/src/integration-test/resources/application-test.yml @@ -0,0 +1,4 @@ +grails: + mail: + host: 127.0.0.1 + port: 3025 \ No newline at end of file diff --git a/src/integration-test/resources/logback-test.xml b/src/integration-test/resources/logback-test.xml index 2f57334..a89a7cc 100644 --- a/src/integration-test/resources/logback-test.xml +++ b/src/integration-test/resources/logback-test.xml @@ -13,4 +13,7 @@ + + + \ No newline at end of file diff --git a/src/main/groovy/grails/plugins/mail/MailMessageBuilder.groovy b/src/main/groovy/grails/plugins/mail/MailMessageBuilder.groovy index e3088cf..0c0c795 100644 --- a/src/main/groovy/grails/plugins/mail/MailMessageBuilder.groovy +++ b/src/main/groovy/grails/plugins/mail/MailMessageBuilder.groovy @@ -15,10 +15,10 @@ */ package grails.plugins.mail -import com.sun.mail.smtp.SMTPMessage import groovy.transform.CompileDynamic import groovy.transform.CompileStatic import groovy.util.logging.Slf4j +import org.eclipse.angus.mail.smtp.SMTPMessage import org.springframework.core.io.ByteArrayResource import org.springframework.core.io.FileSystemResource import org.springframework.core.io.InputStreamSource