From c4d706d971aafb114aa9fd8af43f710fda228321 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Thu, 13 Apr 2017 22:20:47 +0200 Subject: [PATCH 01/15] initial setup --- .editorconfig | 25 ++++++++++++++++++ .gitignore | 5 ++++ README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 README.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6b9d196 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ + +.idea/ +*.iml +target/ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a86ad24 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# Mail Module for asynchronous mail shipping + +Persists the mail in a database and configures a scheduled task to actually send it. +This module is auto configured and depends on spring mail. + +Currently only MongoDB is supported. For other databases implement the `MailStorage` interface. + +## Installation + + + de.upsource + spring-mailing + 1.0.0 + + +## Configuration +See spring mail configuration. + + + +It uses the default ThreadPoolTaskScheduler which comes by default with only one thread. +To configure the scheduler define a configuration class which implements `SchedulingConfigurer`. +For example: + + @Configuration + @EnableScheduling + @Profile("!test") + public class TaskExecutionConfig implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(3); + threadPoolTaskScheduler.initialize(); + taskRegistrar.setTaskScheduler(threadPoolTaskScheduler); + } + } + +## Usage + +Define a Mailer class in your project and inject the `MailService`. + + @Component + public class MyMailer { + + private final MailService mailService; + private final TemplateEngine templateEngine; + private final MessageSource messageSource; + + @Autowired + public MyMailer(MailService mailService, TemplateEngine templateEngine, Environment environment, MessageSource messageSource) { + this.mailService = mailService; + this.templateEngine = templateEngine; + this.messageSource = messageSource; + } + + public void sendMail() { + + Context ctx = createContext(); + + try { + String subject = createSubject(); + String content = createContent(ctx); + + MimeMessage mimeMessage = mailService.createMimeMessage(subject, from, to, content, true); + + mailService.scheduleMail(mimeMessage); + + } catch (Exception e) { + log.error("Could not create mail!", e); + } + } + } From 5728fba0a4ea5046ea61f6c34903f3a97ef0a766 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Thu, 13 Apr 2017 22:23:22 +0200 Subject: [PATCH 02/15] Add MongoDB implementation --- .../postoffice/mongodb/MailDocument.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/main/java/de/nschwalbe/postoffice/mongodb/MailDocument.java diff --git a/src/main/java/de/nschwalbe/postoffice/mongodb/MailDocument.java b/src/main/java/de/nschwalbe/postoffice/mongodb/MailDocument.java new file mode 100644 index 0000000..7bda2ae --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/mongodb/MailDocument.java @@ -0,0 +1,83 @@ +package de.nschwalbe.postoffice.mongodb; + +import java.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.mongodb.core.index.Indexed; +import org.springframework.data.mongodb.core.mapping.Document; + +import de.nschwalbe.postoffice.MailProcessState; +import de.nschwalbe.postoffice.PersistedMail; + +/** + * A mail persisted with mongodb. Mails will be automatically removed after 3 days if send or not. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +@Document(collection = "mails") +class MailDocument implements PersistedMail { + + @Id + private String id; + + @Indexed(expireAfterSeconds = 259200) + @CreatedDate + private LocalDateTime createdDate; + + @LastModifiedDate + private LocalDateTime lastModifiedDate; + + private MailProcessState state = MailProcessState.NOT_SENT; + private String errorMessage; + + private final byte[] mimeMessageContent; + + @PersistenceConstructor + MailDocument(byte[] mimeMessageContent) { + this.mimeMessageContent = mimeMessageContent; + } + + @Override + public String getId() { + return id; + } + + @Override + public LocalDateTime getLastModifiedDate() { + return lastModifiedDate; + } + + @Override + public MailProcessState getState() { + return state; + } + + @Override + public void setState(MailProcessState state) { + this.state = state; + } + + @Override + public String getErrorMessage() { + return errorMessage; + } + + @Override + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public byte[] getMimeMessageContent() { + return mimeMessageContent; + } + + @Override + public LocalDateTime getCreatedDate() { + return createdDate; + } +} From d08340a9fbdd5d92f33d2db63332ee6be71ed248 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Thu, 13 Apr 2017 22:24:08 +0200 Subject: [PATCH 03/15] Add MongoDB implementation --- README.md | 6 +- pom.xml | 73 ++++++++ .../postoffice/MailProcessState.java | 13 ++ .../MailSendingTriggerTaskFactory.java | 169 ++++++++++++++++++ .../de/nschwalbe/postoffice/MailStorage.java | 22 +++ .../nschwalbe/postoffice/PersistedMail.java | 25 +++ .../de/nschwalbe/postoffice/PostOffice.java | 60 +++++++ .../postoffice/PostOfficeConfiguration.java | 52 ++++++ .../postoffice/mongodb/MongoMailStorage.java | 70 ++++++++ .../MongoMailStorageConfiguration.java | 26 +++ src/main/resources/META-INF/spring.factories | 3 + .../mongodb/MongoMailStorageTest.java | 83 +++++++++ 12 files changed, 599 insertions(+), 3 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/de/nschwalbe/postoffice/MailProcessState.java create mode 100644 src/main/java/de/nschwalbe/postoffice/MailSendingTriggerTaskFactory.java create mode 100644 src/main/java/de/nschwalbe/postoffice/MailStorage.java create mode 100644 src/main/java/de/nschwalbe/postoffice/PersistedMail.java create mode 100644 src/main/java/de/nschwalbe/postoffice/PostOffice.java create mode 100644 src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java create mode 100644 src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorage.java create mode 100644 src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageConfiguration.java create mode 100644 src/main/resources/META-INF/spring.factories create mode 100644 src/test/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageTest.java diff --git a/README.md b/README.md index a86ad24..de4b97f 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Currently only MongoDB is supported. For other databases implement the `MailStor ## Installation - de.upsource - spring-mailing - 1.0.0 + de.nschwalbe + postoffice + 0.1.0 ## Configuration diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..521661e --- /dev/null +++ b/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + de.nschwalbe + postoffice + 0.1.0 + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-data-mongodb + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + + + + + com.icegreen + greenmail + 1.5.3 + test + + + junit + junit + + + + + + org.testng + testng + 6.11 + test + + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + test + + + + diff --git a/src/main/java/de/nschwalbe/postoffice/MailProcessState.java b/src/main/java/de/nschwalbe/postoffice/MailProcessState.java new file mode 100644 index 0000000..67bd180 --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/MailProcessState.java @@ -0,0 +1,13 @@ +package de.nschwalbe.postoffice; + +/** + * Defines processing state of an mail. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +public enum MailProcessState { + + NOT_SENT, IN_PROGRESS, FAILED, SENT + +} diff --git a/src/main/java/de/nschwalbe/postoffice/MailSendingTriggerTaskFactory.java b/src/main/java/de/nschwalbe/postoffice/MailSendingTriggerTaskFactory.java new file mode 100644 index 0000000..ae2a973 --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/MailSendingTriggerTaskFactory.java @@ -0,0 +1,169 @@ +package de.nschwalbe.postoffice; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import java.util.List; + +import javax.mail.internet.MimeMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.mail.MailAuthenticationException; +import org.springframework.mail.MailException; +import org.springframework.mail.MailSendException; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.scheduling.Trigger; +import org.springframework.scheduling.TriggerContext; +import org.springframework.scheduling.config.TriggerTask; + +/** + * Mail sending job and trigger. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +class MailSendingTriggerTaskFactory extends AbstractFactoryBean { + + private final MailStorage mailStorage; + private final JavaMailSender javaMailSender; + + MailSendingTriggerTaskFactory(MailStorage mailStorage, JavaMailSender javaMailSender) { + this.mailStorage = mailStorage; + this.javaMailSender = javaMailSender; + } + + @Override + public Class getObjectType() { + return TriggerTask.class; + } + + @Override + protected TriggerTask createInstance() throws Exception { + MailSendingTrigger trigger = new MailSendingTrigger(); + MailSendingTask task = new MailSendingTask(mailStorage, javaMailSender, trigger); + return new TriggerTask(task, trigger); + } + + static class MailSendingTrigger implements Trigger { + + // seconds + private int delay = 30; + + int getDelay() { + return delay; + } + + // try again in 30s, then wait 60s, then wait 90s ... + void increaseDelay() { + delay += 30; + + // max delay between tries is 5 minutes + if (delay > 300) { + delay = 300; + } + } + + + void resetDelay() { + delay = 30; + } + + @Override + public Date nextExecutionTime(TriggerContext triggerContext) { + + Date lastRun = triggerContext.lastCompletionTime(); + ZonedDateTime nextRun; + + if (lastRun == null) { + nextRun = ZonedDateTime.now().plusSeconds(delay); + } + else { + nextRun = ZonedDateTime.ofInstant(lastRun.toInstant(), ZoneId.systemDefault()).plusSeconds(delay); + } + + return Date.from(nextRun.toInstant()); + } + } + + static class MailSendingTask implements Runnable { + + private Logger log = LoggerFactory.getLogger(MailSendingTask.class); + + private final MailStorage mailStorage; + private final JavaMailSender javaMailSender; + private final MailSendingTrigger trigger; + + MailSendingTask(MailStorage mailStorage, JavaMailSender javaMailSender, MailSendingTrigger trigger) { + this.mailStorage = mailStorage; + this.javaMailSender = javaMailSender; + this.trigger = trigger; + } + + public void run() { + + log.trace("Start mail shipping ..."); + + List mailIds = mailStorage.findNotSentIds(); + + if (!mailIds.isEmpty()) { + log.debug("Sending {} mails.", mailIds.size()); + } + + for (String mailId : mailIds) { + + PersistedMail mail = mailStorage.findNotSentAndStartProgress(mailId); + + if (mail == null) { + log.warn("Mail with id {} is not found any more. Skipping it.", mailId); + continue; + } + + if (mail.getState() != MailProcessState.IN_PROGRESS) { + log.warn("Mail with id {} was not set to in_progress but is {}. Skipping it", mailId, mail.getState()); + continue; + } + + try (InputStream in = new ByteArrayInputStream(mail.getMimeMessageContent())) { + + MimeMessage mimeMessage = javaMailSender.createMimeMessage(in); + javaMailSender.send(mimeMessage); + trigger.resetDelay(); + updateMail(mail, MailProcessState.SENT, null); + + } catch (IOException e) { + log.error("Could not create MimeMessage from blob. Email could not be sent!", e); + updateMail(mail, MailProcessState.FAILED, e.getMessage()); + + } catch (MailAuthenticationException e) { + log.error("Could not send mail because of incorrect credentials. __Fix email configuration!__ Trying to send this email again later.", e); + updateMail(mail, MailProcessState.NOT_SENT, e.getMessage()); + trigger.increaseDelay(); + break; // every other mail sending will also be failing + + } catch (MailSendException e) { + log.error("Could not send mail because of a network error! Trying again later.", e); + updateMail(mail, MailProcessState.NOT_SENT, e.getMessage()); + trigger.increaseDelay(); + break; // every other mail sending will also be failing + + } catch (MailException e) { + log.error("Email cannot be send and is thrown away!", e); + updateMail(mail, MailProcessState.FAILED, e.getMessage()); + } + } + + log.trace("Finished mail shipping."); + } + + private void updateMail(PersistedMail mail, MailProcessState state, String message) { + mail.setState(state); + mail.setErrorMessage(message); + mailStorage.update(mail); + } + } +} diff --git a/src/main/java/de/nschwalbe/postoffice/MailStorage.java b/src/main/java/de/nschwalbe/postoffice/MailStorage.java new file mode 100644 index 0000000..72c977e --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/MailStorage.java @@ -0,0 +1,22 @@ +package de.nschwalbe.postoffice; + +import java.util.List; + +/** + * Persists a mail to be send by a scheduled task. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +public interface MailStorage { + + PersistedMail create(byte[] mimeMessageContent); + + List findNotSentIds(); + + PersistedMail findNotSentAndStartProgress(String mailId); + + void delete(String id); + + void update(PersistedMail mail); +} diff --git a/src/main/java/de/nschwalbe/postoffice/PersistedMail.java b/src/main/java/de/nschwalbe/postoffice/PersistedMail.java new file mode 100644 index 0000000..28e6fc7 --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/PersistedMail.java @@ -0,0 +1,25 @@ +package de.nschwalbe.postoffice; + +import java.time.LocalDateTime; + +/** + * Defines a mail to be stored in a database. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +public interface PersistedMail { + + String getId(); + + LocalDateTime getCreatedDate(); + LocalDateTime getLastModifiedDate(); + + byte[] getMimeMessageContent(); + + MailProcessState getState(); + void setState(MailProcessState state); + + String getErrorMessage(); + void setErrorMessage(String message); +} diff --git a/src/main/java/de/nschwalbe/postoffice/PostOffice.java b/src/main/java/de/nschwalbe/postoffice/PostOffice.java new file mode 100644 index 0000000..46de4e5 --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/PostOffice.java @@ -0,0 +1,60 @@ +package de.nschwalbe.postoffice; + +import java.io.ByteArrayOutputStream; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; + +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; + +/** + * Service facade for sending mails. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +public class PostOffice { + + private final MailStorage mailStorage; + private final JavaMailSender mailSender; + + public PostOffice(MailStorage mailStorage, JavaMailSender mailSender) { + this.mailStorage = mailStorage; + this.mailSender = mailSender; + } + + public PersistedMail postMail(MimeMessage mimeMessage) { + + byte[] content; + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + mimeMessage.writeTo(out); + content = out.toByteArray(); + } catch (Exception e) { + throw new IllegalArgumentException("Error reading mime message content!", e); + } + + return mailStorage.create(content); + } + + public MimeMessage createMimeMessage(String subject, String from, String[] to, String content, boolean isHtml) throws MessagingException { + MimeMessageHelper messageHelper = createMimeMessageHelper(subject, from, to); + messageHelper.setText(content, isHtml); + return messageHelper.getMimeMessage(); + } + + public MimeMessage createMimeMessage(String subject, String from, String[] to, String html, String text) throws MessagingException { + MimeMessageHelper messageHelper = createMimeMessageHelper(subject, from, to); + messageHelper.setText(text, html); + return messageHelper.getMimeMessage(); + } + + private MimeMessageHelper createMimeMessageHelper(String subject, String from, String[] to) throws MessagingException { + MimeMessage mimeMessage = mailSender.createMimeMessage(); + MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8"); + message.setSubject(subject); + message.setFrom(from); + message.setTo(to); + return message; + } +} diff --git a/src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java b/src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java new file mode 100644 index 0000000..f23778e --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java @@ -0,0 +1,52 @@ +package de.nschwalbe.postoffice; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; +import org.springframework.scheduling.config.TriggerTask; + +/** + * Configuration of the mailing module. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +@Configuration +public class PostOfficeConfiguration { + + @Autowired + private JavaMailSender javaMailSender; + + @Bean + public PostOffice mailService(MailStorage mailStorage) { + return new PostOffice(mailStorage, javaMailSender); + } + + @Bean("mailSendingTriggerTask") + public MailSendingTriggerTaskFactory mailSendingTriggerTaskFactory(MailStorage mailStorage) { + return new MailSendingTriggerTaskFactory(mailStorage, javaMailSender); + } + + @EnableScheduling + @Configuration + static class SchedulerConfiguration implements SchedulingConfigurer { + + @Autowired + private TriggerTask mailSendingTriggerTask; + + // TODO not for test + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + try { + taskRegistrar.addTriggerTask(mailSendingTriggerTask); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + } +} diff --git a/src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorage.java b/src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorage.java new file mode 100644 index 0000000..3a10bd3 --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorage.java @@ -0,0 +1,70 @@ +package de.nschwalbe.postoffice.mongodb; + +import static org.springframework.data.mongodb.core.query.Criteria.where; +import static org.springframework.data.mongodb.core.query.Query.query; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.data.mongodb.core.FindAndModifyOptions; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import de.nschwalbe.postoffice.MailProcessState; +import de.nschwalbe.postoffice.MailStorage; +import de.nschwalbe.postoffice.PersistedMail; + +/** + * Stores mails in mongodb. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +class MongoMailStorage implements MailStorage { + + private final MongoOperations mongoOperations; + + MongoMailStorage(MongoOperations mongoOperations) { + this.mongoOperations = mongoOperations; + } + + @Override + public MailDocument create(byte[] mimeMessageContent) { + MailDocument mailDocument = new MailDocument(mimeMessageContent); + mongoOperations.save(mailDocument); + return mailDocument; + } + + @Override + public List findNotSentIds() { + + Query query = query(where("state").is(MailProcessState.NOT_SENT)); + query.fields().include("_id"); + + List mailDocuments = mongoOperations.find(query, MailDocument.class); + + return mailDocuments.stream().map(MailDocument::getId).collect(Collectors.toList()); + } + + @Override + public MailDocument findNotSentAndStartProgress(String mailId) { + + Query query = query(where("_id").is(mailId).and("state").is(MailProcessState.NOT_SENT)); + Update update = Update.update("state", MailProcessState.IN_PROGRESS); + return mongoOperations.findAndModify(query, update, FindAndModifyOptions.options().returnNew(true), MailDocument.class); + } + + @Override + public void delete(String id) { + mongoOperations.remove(query(where("_id").is(id)), MailDocument.class); + } + + @Override + public void update(PersistedMail mail) { + if (mail.getId() == null) { + throw new IllegalArgumentException("Cannot update mail because it is not persisted yet."); + } + mongoOperations.save(mail); + } +} diff --git a/src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageConfiguration.java b/src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageConfiguration.java new file mode 100644 index 0000000..5f3c9dd --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageConfiguration.java @@ -0,0 +1,26 @@ +package de.nschwalbe.postoffice.mongodb; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.MongoOperations; + +import de.nschwalbe.postoffice.MailStorage; + +/** + * Configuration for the mongo storage. + * + * @author Nathanael Schwalbe + * @since 06.04.2017 + */ +@ConditionalOnMissingBean(MailStorage.class) +@ConditionalOnClass(MongoOperations.class) +@Configuration +public class MongoMailStorageConfiguration { + + @Bean + public MongoMailStorage mongoMailStorage(MongoOperations mongoOperations) { + return new MongoMailStorage(mongoOperations); + } +} diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..272b3d0 --- /dev/null +++ b/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +de.nschwalbe.postoffice.PostOfficeConfiguration,\ +de.nschwalbe.postoffice.mongodb.MongoMailStorageConfiguration diff --git a/src/test/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageTest.java b/src/test/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageTest.java new file mode 100644 index 0000000..bbac5db --- /dev/null +++ b/src/test/java/de/nschwalbe/postoffice/mongodb/MongoMailStorageTest.java @@ -0,0 +1,83 @@ +package de.nschwalbe.postoffice.mongodb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.charset.Charset; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.annotations.Test; + +import de.nschwalbe.postoffice.PostOfficeConfiguration; +import de.nschwalbe.postoffice.MailProcessState; + +/** + * Integration tests for the mongo mail storage. + * + * @author Nathanael Schwalbe + * @since 05.04.2017 + */ +@SpringBootTest({"spring.mail.host=localhost", "spring.mail.port=2525"}) +public class MongoMailStorageTest extends AbstractTestNGSpringContextTests { + + @Autowired + private MongoMailStorage mongoMailStorage; + + @Autowired + private MongoOperations mongoOperations; + + private String mailId; + + @Test + public void shouldInsertMail() { + + String message = "Email Content"; + MailDocument mailDocument = mongoMailStorage.create(message.getBytes(Charset.forName("UTF-8"))); + + assertThat(mailDocument).isNotNull(); + assertThat(mailDocument.getId()).isNotNull(); + assertThat(mailDocument.getState()).isEqualTo(MailProcessState.NOT_SENT); + + this.mailId = mailDocument.getId(); + boolean exists = mongoOperations.exists(Query.query(Criteria.where("_id").is(mailId)), MailDocument.class); + assertThat(exists).isTrue(); + } + + @Test(dependsOnMethods = "shouldInsertMail") + public void shouldFindNotSentId() { + + List ids = mongoMailStorage.findNotSentIds(); + + assertThat(ids).hasSize(1); + assertThat(ids).contains(mailId); + } + + @Test(dependsOnMethods = "shouldFindNotSentId") + public void shouldFindAndStartProgress() { + + MailDocument mailDocument = mongoMailStorage.findNotSentAndStartProgress(mailId); + + assertThat(mailDocument).isNotNull(); + assertThat(mailDocument.getState()).isEqualTo(MailProcessState.IN_PROGRESS); + } + + @Test(dependsOnMethods = "shouldFindAndStartProgress") + public void shouldDelete() { + + mongoMailStorage.delete(mailId); + boolean exists = mongoOperations.exists(Query.query(Criteria.where("_id").is(mailId)), MailDocument.class); + assertThat(exists).isFalse(); + } + + @SpringBootApplication + @Import({ PostOfficeConfiguration.class}) + static class TestConfiguration { + } +} From 056f161e6715b8c79c5385566a0afdf9f0737792 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Thu, 13 Apr 2017 22:51:38 +0200 Subject: [PATCH 04/15] Add README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de4b97f..2597fcd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# Mail Module for asynchronous mail shipping +# Post Office + +Bring your mail to the post office. It will be stored and a postal worker will deliver it. -Persists the mail in a database and configures a scheduled task to actually send it. This module is auto configured and depends on spring mail. Currently only MongoDB is supported. For other databases implement the `MailStorage` interface. From c7944b378829540e103f4d1920e9987bc49e450d Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Thu, 13 Apr 2017 23:08:19 +0200 Subject: [PATCH 05/15] Add more docs --- README.md | 100 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 2597fcd..7f3bf05 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Post Office Bring your mail to the post office. It will be stored and a postal worker will deliver it. +Because of the always recurring task in every project to store a mail and send it out with a worker I extracted it as a +module. This module is auto configured and depends on spring mail. @@ -8,67 +10,69 @@ Currently only MongoDB is supported. For other databases implement the `MailStor ## Installation - - de.nschwalbe - postoffice - 0.1.0 - +TODO maven dependency from github https://jitpack.io/ + ## Configuration -See spring mail configuration. +### Mail Server +Configure at least the `spring.mail.host` property. For more information see the spring mail configuration. -It uses the default ThreadPoolTaskScheduler which comes by default with only one thread. +### Thread Pool +The default `ThreadPoolTaskScheduler` is used which comes by default with only one thread. To configure the scheduler define a configuration class which implements `SchedulingConfigurer`. For example: - @Configuration - @EnableScheduling - @Profile("!test") - public class TaskExecutionConfig implements SchedulingConfigurer { - - @Override - public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { - ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); - threadPoolTaskScheduler.setPoolSize(3); - threadPoolTaskScheduler.initialize(); - taskRegistrar.setTaskScheduler(threadPoolTaskScheduler); - } +```java +@Configuration +@EnableScheduling +public class TaskExecutionConfig implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(3); + threadPoolTaskScheduler.initialize(); + taskRegistrar.setTaskScheduler(threadPoolTaskScheduler); } +} +``` ## Usage Define a Mailer class in your project and inject the `MailService`. - @Component - public class MyMailer { - - private final MailService mailService; - private final TemplateEngine templateEngine; - private final MessageSource messageSource; - - @Autowired - public MyMailer(MailService mailService, TemplateEngine templateEngine, Environment environment, MessageSource messageSource) { - this.mailService = mailService; - this.templateEngine = templateEngine; - this.messageSource = messageSource; - } - - public void sendMail() { - - Context ctx = createContext(); - - try { - String subject = createSubject(); - String content = createContent(ctx); - - MimeMessage mimeMessage = mailService.createMimeMessage(subject, from, to, content, true); - - mailService.scheduleMail(mimeMessage); - - } catch (Exception e) { - log.error("Could not create mail!", e); - } +```java +@Component +public class MyMailer { + + private final MailService mailService; + private final TemplateEngine templateEngine; + private final MessageSource messageSource; + + @Autowired + public MyMailer(MailService mailService, TemplateEngine templateEngine, Environment environment, MessageSource messageSource) { + this.mailService = mailService; + this.templateEngine = templateEngine; + this.messageSource = messageSource; + } + + public void sendMail() { + + Context ctx = createContext(); + + try { + String subject = createSubject(); + String content = createContent(ctx); + + MimeMessage mimeMessage = mailService.createMimeMessage(subject, from, to, content, true); + + mailService.scheduleMail(mimeMessage); + + } catch (Exception e) { + log.error("Could not create mail!", e); } } +} +``` From b66a5bb56cf860ed0cc7e17a4c76fb277c9b92a6 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Thu, 13 Apr 2017 23:10:39 +0200 Subject: [PATCH 06/15] Add more docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7f3bf05..47da3cf 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Post Office Bring your mail to the post office. It will be stored and a postal worker will deliver it. -Because of the always recurring task in every project to store a mail and send it out with a worker I extracted it as a +Because of the always recurring task in every project to store a mail and send it out with a worker I extracted this as a module. This module is auto configured and depends on spring mail. @@ -68,7 +68,7 @@ public class MyMailer { MimeMessage mimeMessage = mailService.createMimeMessage(subject, from, to, content, true); - mailService.scheduleMail(mimeMessage); + mailService.postMail(mimeMessage); } catch (Exception e) { log.error("Could not create mail!", e); From f38090f583f24a7a3e881af4cd52e59fb7350c2b Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Thu, 13 Apr 2017 23:13:10 +0200 Subject: [PATCH 07/15] Add more docs --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 47da3cf..a187ee3 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,13 @@ Define a Mailer class in your project and inject the `MailService`. @Component public class MyMailer { - private final MailService mailService; + private final PostOffice postOffice; private final TemplateEngine templateEngine; private final MessageSource messageSource; @Autowired - public MyMailer(MailService mailService, TemplateEngine templateEngine, Environment environment, MessageSource messageSource) { - this.mailService = mailService; + public MyMailer(PostOffice postOffice, TemplateEngine templateEngine, Environment environment, MessageSource messageSource) { + this.postOffice = postOffice; this.templateEngine = templateEngine; this.messageSource = messageSource; } @@ -66,9 +66,9 @@ public class MyMailer { String subject = createSubject(); String content = createContent(ctx); - MimeMessage mimeMessage = mailService.createMimeMessage(subject, from, to, content, true); + MimeMessage mimeMessage = postOffice.createMimeMessage(subject, from, to, content, true); - mailService.postMail(mimeMessage); + postOffice.postMail(mimeMessage); } catch (Exception e) { log.error("Could not create mail!", e); From 8f7770c3f3b79e96e00541f789f3aa3bfe85fc98 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Fri, 14 Apr 2017 22:46:04 +0200 Subject: [PATCH 08/15] Generate sources.jar in verify phase --- pom.xml | 73 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 521661e..f833015 100644 --- a/pom.xml +++ b/pom.xml @@ -1,35 +1,35 @@ - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - de.nschwalbe - postoffice - 0.1.0 + de.nschwalbe + postoffice + 0.1.0 - - org.springframework.boot - spring-boot-starter-parent - 1.5.2.RELEASE - - + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + - - UTF-8 - 1.8 - + + UTF-8 + 1.8 + - - - org.springframework.boot - spring-boot-starter-mail - - - org.springframework.boot - spring-boot-starter-data-mongodb + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-data-mongodb true - + @@ -47,7 +47,7 @@ com.icegreen greenmail 1.5.3 - test + test junit @@ -68,6 +68,25 @@ de.flapdoodle.embed.mongo test - + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + verify + + jar-no-fork + + + + + + From 52ca26ad3a86a0526a5ac2085619b55c893efca2 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Fri, 14 Apr 2017 23:57:31 +0200 Subject: [PATCH 09/15] API changes --- pom.xml | 15 +++- .../de/nschwalbe/postoffice/MailAddress.java | 36 ++++++++++ .../de/nschwalbe/postoffice/PostOffice.java | 71 +++++++++++++++++-- 3 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 src/main/java/de/nschwalbe/postoffice/MailAddress.java diff --git a/pom.xml b/pom.xml index f833015..25327e7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ de.nschwalbe postoffice - 0.1.0 + 0.1.1 org.springframework.boot @@ -86,6 +86,19 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + diff --git a/src/main/java/de/nschwalbe/postoffice/MailAddress.java b/src/main/java/de/nschwalbe/postoffice/MailAddress.java new file mode 100644 index 0000000..f3ea3e9 --- /dev/null +++ b/src/main/java/de/nschwalbe/postoffice/MailAddress.java @@ -0,0 +1,36 @@ +package de.nschwalbe.postoffice; + +import java.util.Objects; + +/** + * A mail address. + * + * @author Nathanael Schwalbe + * @since 14.04.2017 + */ +public class MailAddress { + + private final String address; + private final String personal; + + private MailAddress(String address, String personal) { + this.address = Objects.requireNonNull(address); + this.personal = personal; + } + + public static MailAddress of(String address, String personal) { + return new MailAddress(address, personal); + } + + public static MailAddress of(String address) { + return new MailAddress(address, null); + } + + public String getAddress() { + return address; + } + + public String getPersonal() { + return personal; + } +} diff --git a/src/main/java/de/nschwalbe/postoffice/PostOffice.java b/src/main/java/de/nschwalbe/postoffice/PostOffice.java index 46de4e5..e31b6d1 100644 --- a/src/main/java/de/nschwalbe/postoffice/PostOffice.java +++ b/src/main/java/de/nschwalbe/postoffice/PostOffice.java @@ -1,10 +1,17 @@ package de.nschwalbe.postoffice; import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Objects; import javax.mail.MessagingException; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; @@ -16,6 +23,8 @@ */ public class PostOffice { + private static final Logger log = LoggerFactory.getLogger(PostOffice.class); + private final MailStorage mailStorage; private final JavaMailSender mailSender; @@ -24,37 +33,85 @@ public PostOffice(MailStorage mailStorage, JavaMailSender mailSender) { this.mailSender = mailSender; } - public PersistedMail postMail(MimeMessage mimeMessage) { + /** + * Creates a mail, stores it and sends it out later. This method returns immediately and does not wait for the mail server. + * + * @param subject the mail subject. + * @param from the sender address. + * @param to the recipients. + * @param content the mail body. + * @param isHtml true if mail body is html, if it is plain text set to false. + * @return the persisted mail + * @throws MessagingException if message creation failed due to some error. + */ + public PersistedMail postMail(String subject, MailAddress from, List to, String content, boolean isHtml) throws MessagingException { + MimeMessage mimeMessage = createMimeMessage(subject, from, to, content, isHtml); + return postMail(mimeMessage); + } + + /** + * Creates a mail, stores it and sends it out later. This method returns immediately and does not wait for the mail server. + * + * @param subject the mail subject. + * @param from the sender address. + * @param to the recipients. + * @param html the mail body html part. + * @param text the mail body text part. + * @return the persisted mail. + * @throws MessagingException if message creation failed due to some error. + */ + public PersistedMail postMail(String subject, MailAddress from, List to, String html, String text) throws MessagingException { + MimeMessage mimeMessage = createMimeMessage(subject, from, to, html, text); + return postMail(mimeMessage); + } + + private PersistedMail postMail(MimeMessage mimeMessage) throws MessagingException { byte[] content; try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { mimeMessage.writeTo(out); content = out.toByteArray(); } catch (Exception e) { - throw new IllegalArgumentException("Error reading mime message content!", e); + throw new MessagingException("Error reading mime message content!", e); } return mailStorage.create(content); } - public MimeMessage createMimeMessage(String subject, String from, String[] to, String content, boolean isHtml) throws MessagingException { + private MimeMessage createMimeMessage(String subject, MailAddress from, List to, String content, boolean isHtml) throws MessagingException { MimeMessageHelper messageHelper = createMimeMessageHelper(subject, from, to); messageHelper.setText(content, isHtml); return messageHelper.getMimeMessage(); } - public MimeMessage createMimeMessage(String subject, String from, String[] to, String html, String text) throws MessagingException { + private MimeMessage createMimeMessage(String subject, MailAddress from, List to, String html, String text) throws MessagingException { MimeMessageHelper messageHelper = createMimeMessageHelper(subject, from, to); messageHelper.setText(text, html); return messageHelper.getMimeMessage(); } - private MimeMessageHelper createMimeMessageHelper(String subject, String from, String[] to) throws MessagingException { + private MimeMessageHelper createMimeMessageHelper(String subject, MailAddress from, List to) throws MessagingException { MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8"); message.setSubject(subject); - message.setFrom(from); - message.setTo(to); + try { + message.setFrom(from.getAddress(), from.getPersonal()); + } catch (UnsupportedEncodingException e) { + throw new MessagingException("Error creating From-Address", e); + } + + InternetAddress[] internetAddresses = to.stream() + .map(mailAddress -> { + try { + return new InternetAddress(mailAddress.getAddress(), mailAddress.getPersonal(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + log.error("This should never happen!", e); + return null; + } + }) + .filter(Objects::nonNull) + .toArray(InternetAddress[]::new); + message.setTo(internetAddresses); return message; } } From fd799081f49c09f219534ba3231c61b813c4947c Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Sat, 15 Apr 2017 00:05:07 +0200 Subject: [PATCH 10/15] Add jitpack badge --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a187ee3..5e7b59b 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,6 @@ public class MyMailer { } } ``` + +[![Release](https://jitpack.io/v/nschwalbe/postoffice.svg)] +(https://jitpack.io/#nschwalbe/postoffice) From 9d76bce3b57f6b39cd23b948865f41617ce4aeec Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Sat, 15 Apr 2017 00:14:04 +0200 Subject: [PATCH 11/15] Try jitpack flat style icon --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e7b59b..23845ff 100644 --- a/README.md +++ b/README.md @@ -77,5 +77,5 @@ public class MyMailer { } ``` -[![Release](https://jitpack.io/v/nschwalbe/postoffice.svg)] +[![Release](https://jitpack.io/v/nschwalbe/postoffice.svg?style=flat-square)] (https://jitpack.io/#nschwalbe/postoffice) From 8a4476f4f5a71e45db075bca36aaa24b0770053a Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Sat, 15 Apr 2017 00:25:08 +0200 Subject: [PATCH 12/15] Fix markdown --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 23845ff..64fa866 100644 --- a/README.md +++ b/README.md @@ -77,5 +77,4 @@ public class MyMailer { } ``` -[![Release](https://jitpack.io/v/nschwalbe/postoffice.svg?style=flat-square)] -(https://jitpack.io/#nschwalbe/postoffice) +[![Release](https://jitpack.io/v/nschwalbe/postoffice.svg?style=flat-square)](https://jitpack.io/#nschwalbe/postoffice) From 52882729994265304b920a3adfae0c891f725f1f Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Sat, 15 Apr 2017 00:34:06 +0200 Subject: [PATCH 13/15] Add jitpack repo documentation --- README.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 64fa866..e1f019f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,32 @@ Currently only MongoDB is supported. For other databases implement the `MailStor ## Installation -TODO maven dependency from github https://jitpack.io/ +[![Release](https://jitpack.io/v/nschwalbe/postoffice.svg?style=flat-square)](https://jitpack.io/#nschwalbe/postoffice) + +The module is build by jitpack and can be downloaded with maven or gradle. + +Add the jitpack repository: + +```xml + + + jitpack.io + https://jitpack.io + + +``` + +And dependency: + +```xml + + com.github.nschwalbe + postoffice + develop-SNAPSHOT + +``` + +For more information like gradle see here: ## Configuration @@ -77,4 +102,3 @@ public class MyMailer { } ``` -[![Release](https://jitpack.io/v/nschwalbe/postoffice.svg?style=flat-square)](https://jitpack.io/#nschwalbe/postoffice) From 90816f47f7d45c75ca62c280249c5859fe915e0b Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Sun, 16 Apr 2017 22:38:39 +0200 Subject: [PATCH 14/15] Change api for single to address --- pom.xml | 2 +- .../de/nschwalbe/postoffice/PostOffice.java | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 25327e7..e9b39aa 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ de.nschwalbe postoffice - 0.1.1 + 0.1.2 org.springframework.boot diff --git a/src/main/java/de/nschwalbe/postoffice/PostOffice.java b/src/main/java/de/nschwalbe/postoffice/PostOffice.java index e31b6d1..14c320c 100644 --- a/src/main/java/de/nschwalbe/postoffice/PostOffice.java +++ b/src/main/java/de/nschwalbe/postoffice/PostOffice.java @@ -2,6 +2,7 @@ import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -33,6 +34,38 @@ public PostOffice(MailStorage mailStorage, JavaMailSender mailSender) { this.mailSender = mailSender; } + /** + * Creates a mail, stores it and sends it out later. This method returns immediately and does not wait for the mail server. + * + * @param subject the mail subject. + * @param from the sender address. + * @param to the recipient. + * @param content the mail body. + * @param isHtml true if mail body is html, if it is plain text set to false. + * @return the persisted mail + * @throws MessagingException if message creation failed due to some error. + */ + public PersistedMail postMail(String subject, MailAddress from, MailAddress to, String content, boolean isHtml) throws MessagingException { + MimeMessage mimeMessage = createMimeMessage(subject, from, Collections.singletonList(to), content, isHtml); + return postMail(mimeMessage); + } + + /** + * Creates a mail, stores it and sends it out later. This method returns immediately and does not wait for the mail server. + * + * @param subject the mail subject. + * @param from the sender address. + * @param to the recipient. + * @param html the mail body html part. + * @param text the mail body text part. + * @return the persisted mail. + * @throws MessagingException if message creation failed due to some error. + */ + public PersistedMail postMail(String subject, MailAddress from, MailAddress to, String html, String text) throws MessagingException { + MimeMessage mimeMessage = createMimeMessage(subject, from, Collections.singletonList(to), html, text); + return postMail(mimeMessage); + } + /** * Creates a mail, stores it and sends it out later. This method returns immediately and does not wait for the mail server. * From dca651c4270ca5f1e85a40cb27e5a63a449f4b93 Mon Sep 17 00:00:00 2001 From: Nathanael Schwalbe Date: Tue, 18 Apr 2017 11:55:59 +0200 Subject: [PATCH 15/15] Renaming send mail task --- pom.xml | 2 +- .../postoffice/PostOfficeConfiguration.java | 17 ++++++---- ...kFactory.java => SendMailTaskFactory.java} | 33 +++++++++++-------- 3 files changed, 30 insertions(+), 22 deletions(-) rename src/main/java/de/nschwalbe/postoffice/{MailSendingTriggerTaskFactory.java => SendMailTaskFactory.java} (83%) diff --git a/pom.xml b/pom.xml index e9b39aa..c731112 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ de.nschwalbe postoffice - 0.1.2 + 0.1.3 org.springframework.boot diff --git a/src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java b/src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java index f23778e..9040e51 100644 --- a/src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java +++ b/src/main/java/de/nschwalbe/postoffice/PostOfficeConfiguration.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; @@ -21,14 +22,17 @@ public class PostOfficeConfiguration { @Autowired private JavaMailSender javaMailSender; + @Autowired + private Environment env; + @Bean - public PostOffice mailService(MailStorage mailStorage) { + public PostOffice postOffice(MailStorage mailStorage) { return new PostOffice(mailStorage, javaMailSender); } - @Bean("mailSendingTriggerTask") - public MailSendingTriggerTaskFactory mailSendingTriggerTaskFactory(MailStorage mailStorage) { - return new MailSendingTriggerTaskFactory(mailStorage, javaMailSender); + @Bean("sendMailTask") + public SendMailTaskFactory sendMailTaskFactory(MailStorage mailStorage) { + return new SendMailTaskFactory(mailStorage, javaMailSender, env); } @EnableScheduling @@ -36,13 +40,12 @@ public MailSendingTriggerTaskFactory mailSendingTriggerTaskFactory(MailStorage m static class SchedulerConfiguration implements SchedulingConfigurer { @Autowired - private TriggerTask mailSendingTriggerTask; + private TriggerTask sendMailTask; - // TODO not for test @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { try { - taskRegistrar.addTriggerTask(mailSendingTriggerTask); + taskRegistrar.addTriggerTask(sendMailTask); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/de/nschwalbe/postoffice/MailSendingTriggerTaskFactory.java b/src/main/java/de/nschwalbe/postoffice/SendMailTaskFactory.java similarity index 83% rename from src/main/java/de/nschwalbe/postoffice/MailSendingTriggerTaskFactory.java rename to src/main/java/de/nschwalbe/postoffice/SendMailTaskFactory.java index ae2a973..970fc08 100644 --- a/src/main/java/de/nschwalbe/postoffice/MailSendingTriggerTaskFactory.java +++ b/src/main/java/de/nschwalbe/postoffice/SendMailTaskFactory.java @@ -13,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.core.env.Environment; import org.springframework.mail.MailAuthenticationException; import org.springframework.mail.MailException; import org.springframework.mail.MailSendException; @@ -27,14 +28,16 @@ * @author Nathanael Schwalbe * @since 05.04.2017 */ -class MailSendingTriggerTaskFactory extends AbstractFactoryBean { +class SendMailTaskFactory extends AbstractFactoryBean { private final MailStorage mailStorage; private final JavaMailSender javaMailSender; + private final Environment env; - MailSendingTriggerTaskFactory(MailStorage mailStorage, JavaMailSender javaMailSender) { + SendMailTaskFactory(MailStorage mailStorage, JavaMailSender javaMailSender, Environment env) { this.mailStorage = mailStorage; this.javaMailSender = javaMailSender; + this.env = env; } @Override @@ -44,18 +47,20 @@ public Class getObjectType() { @Override protected TriggerTask createInstance() throws Exception { - MailSendingTrigger trigger = new MailSendingTrigger(); - MailSendingTask task = new MailSendingTask(mailStorage, javaMailSender, trigger); + SendMailTrigger trigger = new SendMailTrigger(env.getProperty("postoffice.worker.delay", Integer.class, 10)); + SendMailTask task = new SendMailTask(mailStorage, javaMailSender, trigger); return new TriggerTask(task, trigger); } - static class MailSendingTrigger implements Trigger { + static class SendMailTrigger implements Trigger { - // seconds - private int delay = 30; + private int defaultDelay; + private int delay; - int getDelay() { - return delay; + + SendMailTrigger(int delay) { + this.defaultDelay = delay; + this.delay = delay; } // try again in 30s, then wait 60s, then wait 90s ... @@ -70,7 +75,7 @@ void increaseDelay() { void resetDelay() { - delay = 30; + delay = defaultDelay; } @Override @@ -90,15 +95,15 @@ public Date nextExecutionTime(TriggerContext triggerContext) { } } - static class MailSendingTask implements Runnable { + static class SendMailTask implements Runnable { - private Logger log = LoggerFactory.getLogger(MailSendingTask.class); + private Logger log = LoggerFactory.getLogger(SendMailTask.class); private final MailStorage mailStorage; private final JavaMailSender javaMailSender; - private final MailSendingTrigger trigger; + private final SendMailTrigger trigger; - MailSendingTask(MailStorage mailStorage, JavaMailSender javaMailSender, MailSendingTrigger trigger) { + SendMailTask(MailStorage mailStorage, JavaMailSender javaMailSender, SendMailTrigger trigger) { this.mailStorage = mailStorage; this.javaMailSender = javaMailSender; this.trigger = trigger;