Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 13600c4

Browse files
implement sending email using Java Mail
1 parent d987a9b commit 13600c4

File tree

13 files changed

+406
-1
lines changed

13 files changed

+406
-1
lines changed

‎pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@
4343
<groupId>org.springframework.boot</groupId>
4444
<artifactId>spring-boot-starter-security</artifactId>
4545
</dependency>
46+
<dependency>
47+
<groupId>org.springframework.boot</groupId>
48+
<artifactId>spring-boot-starter-mail</artifactId>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.springframework.boot</groupId>
52+
<artifactId>spring-boot-configuration-processor</artifactId>
53+
<optional>true</optional>
54+
</dependency>
4655
<dependency>
4756
<groupId>mysql</groupId>
4857
<artifactId>mysql-connector-java</artifactId>
@@ -56,6 +65,10 @@
5665
<artifactId>commons-io</artifactId>
5766
<version>${commons-io.version}</version>
5867
</dependency>
68+
<dependency>
69+
<groupId>org.freemarker</groupId>
70+
<artifactId>freemarker</artifactId>
71+
</dependency>
5972

6073
<dependency>
6174
<groupId>org.springframework.boot</groupId>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.taskagile.config;
2+
3+
import javax.validation.constraints.Email;
4+
import javax.validation.constraints.NotBlank;
5+
import org.springframework.boot.context.properties.ConfigurationProperties;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.validation.annotation.Validated;
8+
9+
@Configuration
10+
@ConfigurationProperties(prefix="app")
11+
@Validated
12+
public class ApplicationProperties {
13+
14+
/**
15+
* Default `from` value of emails sent out by the system
16+
*/
17+
@Email
18+
@NotBlank
19+
private String mailFrom;
20+
21+
public void setMailFrom(String mailFrom) {
22+
this.mailFrom = mailFrom;
23+
}
24+
25+
public String getMailFrom() {
26+
return mailFrom;
27+
}
28+
}
Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,59 @@
11
package com.taskagile.domain.common.mail;
22

3+
import freemarker.template.Configuration;
4+
import freemarker.template.Template;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import org.springframework.beans.factory.annotation.Value;
38
import org.springframework.stereotype.Component;
9+
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
10+
import org.springframework.util.Assert;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
414

515
@Component
616
public class DefaultMailManager implements MailManager {
717

18+
private final static Logger log = LoggerFactory.getLogger(DefaultMailManager.class);
19+
20+
private String mailFrom;
21+
private Mailer mailer;
22+
private Configuration configuration;
23+
24+
public DefaultMailManager(@Value("${app.mail-from}") String mailFrom,
25+
Mailer mailer,
26+
Configuration configuration) {
27+
this.mailFrom = mailFrom;
28+
this.mailer = mailer;
29+
this.configuration = configuration;
30+
}
31+
832
@Override
933
public void send(String emailAddress, String subject, String template, MessageVariable... variables) {
10-
// TODO implement this
34+
Assert.hasText(emailAddress, "Parameter `emailAddress` must not be blank");
35+
Assert.hasText(subject, "Parameter `subject` must not be blank");
36+
Assert.hasText(template, "Parameter `template` must not be blank");
37+
38+
String messageBody = createMessageBody(template, variables);
39+
Message message = new SimpleMessage(emailAddress, subject, messageBody, mailFrom);
40+
mailer.send(message);
41+
}
42+
43+
private String createMessageBody(String templateName, MessageVariable... variables) {
44+
try {
45+
Template template = configuration.getTemplate(templateName);
46+
Map<String, Object> model = new HashMap<>();
47+
if (variables != null) {
48+
for (MessageVariable variable : variables) {
49+
model.put(variable.getKey(), variable.getValue());
50+
}
51+
}
52+
return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
53+
} catch (Exception e) {
54+
log.error("Failed to create message body from template `" + templateName + "`", e);
55+
return null;
56+
}
1157
}
58+
1259
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.taskagile.domain.common.mail;
2+
3+
public interface Mailer {
4+
5+
/**
6+
* Send a message
7+
*
8+
* @param message the message instance
9+
*/
10+
void send(Message message);
11+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.taskagile.domain.common.mail;
2+
3+
public interface Message {
4+
5+
/**
6+
* Get the recipient of the message
7+
*
8+
* @return recipient's email address
9+
*/
10+
String getTo();
11+
12+
/**
13+
* Get the subject of the message
14+
*
15+
* @return message's subject
16+
*/
17+
String getSubject();
18+
19+
/**
20+
* Get the body of the message
21+
*
22+
* @return the body of the message
23+
*/
24+
String getBody();
25+
26+
/**
27+
* Get the from of this message
28+
*
29+
* @return where this message is from
30+
*/
31+
String getFrom();
32+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.taskagile.domain.common.mail;
2+
3+
import java.util.Objects;
4+
5+
public class SimpleMessage implements Message {
6+
7+
private String to;
8+
private String subject;
9+
private String body;
10+
private String from;
11+
12+
public SimpleMessage(String to, String subject, String body, String from) {
13+
this.to = to;
14+
this.subject = subject;
15+
this.body = body;
16+
this.from = from;
17+
}
18+
19+
@Override
20+
public String getTo() {
21+
return to;
22+
}
23+
24+
@Override
25+
public String getSubject() {
26+
return subject;
27+
}
28+
29+
@Override
30+
public String getBody() {
31+
return body;
32+
}
33+
34+
public String getFrom() {
35+
return from;
36+
}
37+
38+
@Override
39+
public boolean equals(Object o) {
40+
if (this == o) return true;
41+
if (!(o instanceof SimpleMessage)) return false;
42+
SimpleMessage that = (SimpleMessage) o;
43+
return Objects.equals(to, that.to) &&
44+
Objects.equals(subject, that.subject) &&
45+
Objects.equals(body, that.body);
46+
}
47+
48+
@Override
49+
public int hashCode() {
50+
return Objects.hash(to, subject, body);
51+
}
52+
53+
@Override
54+
public String toString() {
55+
return "SimpleMessage{" +
56+
"to='" + to + '\'' +
57+
", subject='" + subject + '\'' +
58+
", body='" + body + '\'' +
59+
'}';
60+
}
61+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.taskagile.infrastructure.mail;
2+
3+
import com.taskagile.domain.common.mail.Mailer;
4+
import com.taskagile.domain.common.mail.Message;
5+
import org.apache.commons.lang3.StringUtils;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
import org.springframework.mail.MailException;
9+
import org.springframework.mail.SimpleMailMessage;
10+
import org.springframework.mail.javamail.JavaMailSender;
11+
import org.springframework.scheduling.annotation.Async;
12+
import org.springframework.stereotype.Component;
13+
import org.springframework.util.Assert;
14+
15+
@Component
16+
public class AsyncMailer implements Mailer {
17+
18+
private static final Logger log = LoggerFactory.getLogger(AsyncMailer.class);
19+
20+
private JavaMailSender mailSender;
21+
22+
public AsyncMailer(JavaMailSender mailSender) {
23+
this.mailSender = mailSender;
24+
}
25+
26+
@Async
27+
@Override
28+
public void send(Message message) {
29+
Assert.notNull(message, "Parameter `message` must not be null");
30+
31+
try {
32+
SimpleMailMessage mailMessage = new SimpleMailMessage();
33+
34+
if (StringUtils.isNotBlank(message.getFrom())) {
35+
mailMessage.setFrom(message.getFrom());
36+
}
37+
if (StringUtils.isNotBlank(message.getSubject())) {
38+
mailMessage.setSubject(message.getSubject());
39+
}
40+
if (StringUtils.isNotEmpty(message.getBody())) {
41+
mailMessage.setText(message.getBody());
42+
}
43+
if (message.getTo() != null) {
44+
mailMessage.setTo(message.getTo());
45+
}
46+
47+
mailSender.send(mailMessage);
48+
} catch (MailException e) {
49+
log.error("Failed to send mail message", e);
50+
}
51+
}
52+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
app.mail-from=noreply@taskagile.com
2+
13
spring.datasource.url=jdbc:mysql://localhost:3306/task_agile?useSSL=false
24
spring.datasource.username=<username>
35
spring.datasource.password=<password>
46
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
57

8+
spring.jpa.open-in-view=false
9+
spring.jpa.hibernate.ddl-auto=none
10+
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
11+
12+
spring.freemarker.template-loader-path=classpath:/mail-templates/
13+
14+
spring.mail.host=localhost
15+
spring.mail.port=1025
16+
spring.mail.properties.mail.smtp.auth=false
17+
618
logging.level.com.taskagile=DEBUG
719
logging.level.org.springframework.security=DEBUG
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<h1>Welcome!</h1>
2+
<p>Here is your registration information:</p>
3+
<ul>
4+
<li>Username: ${user.username}</li>
5+
<li>Email Address: ${user.emailAddress}</li>
6+
</ul>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.taskagile.domain.common.mail;
2+
3+
import freemarker.template.Configuration;
4+
import org.junit.Before;
5+
import org.junit.Test;
6+
import org.junit.runner.RunWith;
7+
import org.mockito.ArgumentCaptor;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.boot.test.context.TestConfiguration;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.test.context.ActiveProfiles;
12+
import org.springframework.test.context.junit4.SpringRunner;
13+
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean;
14+
15+
import static org.junit.Assert.assertEquals;
16+
import static org.mockito.Mockito.mock;
17+
import static org.mockito.Mockito.verify;
18+
19+
@RunWith(SpringRunner.class)
20+
@ActiveProfiles("test")
21+
public class DefaultMailManagerTests {
22+
23+
@TestConfiguration
24+
static class DefaultMessageCreatorConfiguration {
25+
@Bean
26+
public FreeMarkerConfigurationFactoryBean getFreemarkerConfiguration() {
27+
FreeMarkerConfigurationFactoryBean factoryBean = new FreeMarkerConfigurationFactoryBean();
28+
factoryBean.setTemplateLoaderPath("/mail-templates/");
29+
return factoryBean;
30+
}
31+
}
32+
33+
@Autowired
34+
private Configuration configuration;
35+
private Mailer mailerMock;
36+
private DefaultMailManager instance;
37+
38+
@Before
39+
public void setUp() {
40+
mailerMock = mock(Mailer.class);
41+
instance = new DefaultMailManager("noreply@taskagile.com", mailerMock, configuration);
42+
}
43+
44+
@Test(expected = IllegalArgumentException.class)
45+
public void send_nullEmailAddress_shouldFail() {
46+
instance.send(null, "Test subject", "test.ftl");
47+
}
48+
49+
@Test(expected = IllegalArgumentException.class)
50+
public void send_emptyEmailAddress_shouldFail() {
51+
instance.send("", "Test subject", "test.ftl");
52+
}
53+
54+
@Test(expected = IllegalArgumentException.class)
55+
public void send_nullSubject_shouldFail() {
56+
instance.send("test@taskagile.com", null, "test.ftl");
57+
}
58+
59+
@Test(expected = IllegalArgumentException.class)
60+
public void send_emptySubject_shouldFail() {
61+
instance.send("test@taskagile.com", "", "test.ftl");
62+
}
63+
64+
@Test(expected = IllegalArgumentException.class)
65+
public void send_nullTemplateName_shouldFail() {
66+
instance.send("test@taskagile.com", "Test subject", null);
67+
}
68+
69+
@Test(expected = IllegalArgumentException.class)
70+
public void send_emptyTemplateName_shouldFail() {
71+
instance.send("test@taskagile.com", "Test subject", "");
72+
}
73+
74+
@Test
75+
public void send_validParameters_shouldSucceed() {
76+
String to = "user@example.com";
77+
String subject = "Test subject";
78+
String templateName = "test.ftl";
79+
80+
instance.send(to, subject, templateName, MessageVariable.from("name", "test"));
81+
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
82+
verify(mailerMock).send(messageArgumentCaptor.capture());
83+
84+
Message messageSent = messageArgumentCaptor.getValue();
85+
assertEquals(to, messageSent.getTo());
86+
assertEquals(subject, messageSent.getSubject());
87+
assertEquals("noreply@taskagile.com", messageSent.getFrom());
88+
assertEquals("Hello, test\n", messageSent.getBody());
89+
}
90+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /