├── src └── main │ ├── resources │ ├── templates │ │ ├── email-subject.vm │ │ └── email-body.vm │ └── application.properties │ └── java │ └── es │ └── urjc │ └── code │ └── dad │ └── mail │ └── batch │ ├── MailBatchApplication.java │ ├── MailBatchItemWriter.java │ ├── model │ └── Student.java │ ├── JobCompletionNotificationListener.java │ ├── StudentItemProcessor.java │ └── BatchConfiguration.java ├── sample-attachment.txt ├── data.csv ├── .gitignore ├── README.md └── pom.xml /src/main/resources/templates/email-subject.vm: -------------------------------------------------------------------------------- 1 | Your code -------------------------------------------------------------------------------- /sample-attachment.txt: -------------------------------------------------------------------------------- 1 | You should've received this file attached to your mail -------------------------------------------------------------------------------- /data.csv: -------------------------------------------------------------------------------- 1 | "Full name","Code","Email" 2 | "Pete Peterson","M500","test@example.com" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /mvnw 3 | /mvnw.cmd 4 | .classpath 5 | .project 6 | .mvn 7 | .settings 8 | -------------------------------------------------------------------------------- /src/main/resources/templates/email-body.vm: -------------------------------------------------------------------------------- 1 | Hi, $name 2 | 3 | This is your code: $code 4 | 5 | Find instructions attached to this email. 6 | 7 | Regards. -------------------------------------------------------------------------------- /src/main/java/es/urjc/code/dad/mail/batch/MailBatchApplication.java: -------------------------------------------------------------------------------- 1 | package es.urjc.code.dad.mail.batch; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MailBatchApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MailBatchApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #springboot-starter-mail properties 2 | #spring.mail.host= 3 | #spring.mail.port= 4 | #spring.mail.username= 5 | #spring.mail.password= 6 | spring.mail.test-connection=true 7 | 8 | # Change this according to your mail server if necessary 9 | spring.mail.properties.mail.smtp.auth=true 10 | spring.mail.properties.mail.transport.protocol=tls 11 | spring.mail.properties.mail.smtp.starttls.enable=true 12 | 13 | # Path to csv data file (first line will be skipped) 14 | #codeurjc.batch.data= 15 | # Path to attachment data (this file will be attached to all emails sent) 16 | #codeurjc.batch.attachment= 17 | 18 | # email for job notifications 19 | # codeurjc.batch.notifications.email= -------------------------------------------------------------------------------- /src/main/java/es/urjc/code/dad/mail/batch/MailBatchItemWriter.java: -------------------------------------------------------------------------------- 1 | package es.urjc.code.dad.mail.batch; 2 | 3 | import java.util.List; 4 | 5 | import javax.mail.internet.MimeMessage; 6 | 7 | import org.springframework.batch.item.ItemWriter; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.mail.javamail.JavaMailSender; 10 | 11 | public class MailBatchItemWriter implements ItemWriter { 12 | 13 | @Autowired 14 | private JavaMailSender mailSender; 15 | 16 | @Override 17 | public void write(List messages) throws Exception { 18 | 19 | messages.stream().forEach((message)->mailSender.send(message)); 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/es/urjc/code/dad/mail/batch/model/Student.java: -------------------------------------------------------------------------------- 1 | package es.urjc.code.dad.mail.batch.model; 2 | 3 | public class Student { 4 | 5 | private String fullname; 6 | private String code; 7 | private String email; 8 | 9 | public Student() { 10 | } 11 | 12 | public String getFullname() { 13 | return fullname; 14 | } 15 | 16 | public void setFullname(String fullname) { 17 | this.fullname = fullname; 18 | } 19 | 20 | public String getCode() { 21 | return code; 22 | } 23 | 24 | public void setCode(String code) { 25 | this.code = code; 26 | } 27 | 28 | public String getEmail() { 29 | return email; 30 | } 31 | 32 | public void setEmail(String email) { 33 | this.email = email; 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Mail batch application 3 | 4 | A sample Spring Batch application to send emails to several recipients. It feeds email addresses from a csv file and sends an email to each recipient including an attachment. A sample csv file and attachment file are provided as samples. The code uses the [Spring Batch](http://projects.spring.io/spring-batch/ "Spring Batch") project to configure a job for this task. 5 | 6 | I've been using this application to send personal codes and specific instructions on how to register to some PaaS and IaaS providers to my students. The project is tailored to this specific use case, where instructions are sent as an attachment and the body of the email contains the personal code to redeem in order to get student access to some cloud provider. 7 | 8 | ## How to run the application 9 | 10 | Java 8 is required. To run the application: 11 | 12 | mvn clean package 13 | java -jar target/mail-batch-0.0.1-SNAPSHOT.jar \ 14 | --spring.mail.host= \ 15 | --spring.mail.port= \ 16 | --spring.mail.username= \ 17 | --spring.mail.password= \ 18 | --codeurjc.batch.data=data.csv \ 19 | --codeurjc.batch.attachment=sample-attachment.txt 20 | 21 | ## Release changes 22 | 23 | **0.2.0** 24 | 25 | * Add velocity templates for building email subject and body 26 | 27 | **0.1.0** 28 | 29 | * Initial implementation -------------------------------------------------------------------------------- /src/main/java/es/urjc/code/dad/mail/batch/JobCompletionNotificationListener.java: -------------------------------------------------------------------------------- 1 | package es.urjc.code.dad.mail.batch; 2 | 3 | import org.springframework.batch.core.JobExecution; 4 | import org.springframework.batch.core.listener.JobExecutionListenerSupport; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.mail.SimpleMailMessage; 8 | import org.springframework.mail.javamail.JavaMailSender; 9 | 10 | public class JobCompletionNotificationListener extends JobExecutionListenerSupport { 11 | 12 | @Autowired 13 | private JavaMailSender mailSender; 14 | 15 | private String notificationEmail; 16 | 17 | public JobCompletionNotificationListener(String notificationEmail) { 18 | this.notificationEmail = notificationEmail; 19 | } 20 | 21 | @Override 22 | public void afterJob(JobExecution jobExecution) { 23 | 24 | SimpleMailMessage message = new SimpleMailMessage(); 25 | 26 | message.setSubject("Job completion"); 27 | message.setFrom(notificationEmail); 28 | message.setTo(notificationEmail); 29 | message.setText("Job completed with " + jobExecution.getExitStatus()); 30 | 31 | mailSender.send(message); 32 | } 33 | 34 | @Override 35 | public void beforeJob(JobExecution jobExecution) { 36 | 37 | SimpleMailMessage message = new SimpleMailMessage(); 38 | 39 | message.setSubject("Job started"); 40 | message.setFrom(notificationEmail); 41 | message.setTo(notificationEmail); 42 | message.setText("Job started"); 43 | 44 | mailSender.send(message); 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | es.urjc.code.dad 7 | mail-batch 8 | 0.4.0-SNAPSHOT 9 | jar 10 | 11 | mail-batch 12 | Send mails with Spring Batch project 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-batch 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-mail 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-test 39 | test 40 | 41 | 42 | org.apache.velocity 43 | velocity 44 | 45 | 46 | org.scala-lang 47 | scala-library 48 | 2.11.0 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/es/urjc/code/dad/mail/batch/StudentItemProcessor.java: -------------------------------------------------------------------------------- 1 | package es.urjc.code.dad.mail.batch; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javax.mail.internet.MimeMessage; 7 | 8 | import org.apache.velocity.app.VelocityEngine; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.batch.item.ItemProcessor; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.core.io.FileSystemResource; 14 | import org.springframework.mail.javamail.JavaMailSender; 15 | import org.springframework.mail.javamail.MimeMessageHelper; 16 | import org.springframework.ui.velocity.VelocityEngineUtils; 17 | 18 | import es.urjc.code.dad.mail.batch.model.Student; 19 | 20 | public class StudentItemProcessor implements ItemProcessor { 21 | 22 | private static final Logger log = LoggerFactory.getLogger(StudentItemProcessor.class); 23 | 24 | @Autowired 25 | private JavaMailSender mailSender; 26 | @Autowired 27 | private VelocityEngine engine; 28 | private String sender; 29 | private String attachment; 30 | 31 | public StudentItemProcessor(String sender, String attachment) { 32 | this.sender = sender; 33 | this.attachment = attachment; 34 | } 35 | 36 | @Override 37 | public MimeMessage process(Student student) throws Exception { 38 | MimeMessage message = mailSender.createMimeMessage(); 39 | 40 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 41 | 42 | Map model = new HashMap<>(); 43 | model.put("name", student.getFullname()); 44 | model.put("code", student.getCode()); 45 | helper.setFrom(sender); 46 | helper.setTo(student.getEmail()); 47 | helper.setCc(sender); 48 | helper.setSubject(VelocityEngineUtils.mergeTemplateIntoString(engine, "email-subject.vm", "UTF-8", model)); 49 | helper.setText(VelocityEngineUtils.mergeTemplateIntoString(engine, "email-body.vm", "UTF-8", model)); 50 | 51 | log.info("Preparing message for: " + student.getEmail()); 52 | 53 | FileSystemResource file = new FileSystemResource(attachment); 54 | helper.addAttachment(file.getFilename(), file); 55 | 56 | return message; 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/es/urjc/code/dad/mail/batch/BatchConfiguration.java: -------------------------------------------------------------------------------- 1 | package es.urjc.code.dad.mail.batch; 2 | 3 | import javax.mail.internet.MimeMessage; 4 | 5 | import org.springframework.batch.core.Job; 6 | import org.springframework.batch.core.JobExecutionListener; 7 | import org.springframework.batch.core.Step; 8 | import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; 9 | import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; 10 | import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; 11 | import org.springframework.batch.core.launch.support.RunIdIncrementer; 12 | import org.springframework.batch.item.file.FlatFileItemReader; 13 | import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; 14 | import org.springframework.batch.item.file.mapping.DefaultLineMapper; 15 | import org.springframework.batch.item.file.transform.DelimitedLineTokenizer; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.beans.factory.annotation.Value; 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.core.io.FileSystemResource; 21 | 22 | import es.urjc.code.dad.mail.batch.model.Student; 23 | 24 | @Configuration 25 | @EnableBatchProcessing 26 | public class BatchConfiguration { 27 | 28 | @Autowired 29 | public JobBuilderFactory jobBuilderFactory; 30 | 31 | @Autowired 32 | public StepBuilderFactory stepBuilderFactory; 33 | 34 | @Value("${spring.mail.username}") 35 | private String sender; 36 | 37 | @Value("${codeurjc.batch.data}") 38 | public String data; 39 | 40 | @Value("${codeurjc.batch.attachment}") 41 | private String attachment; 42 | 43 | @Value("${codeurjc.batch.notifications.email}") 44 | private String email; 45 | 46 | // tag::readerwriterprocessor[] 47 | @Bean 48 | public FlatFileItemReader reader() { 49 | FlatFileItemReader reader = new FlatFileItemReader<>(); 50 | reader.setResource(new FileSystemResource(data)); 51 | reader.setLinesToSkip(1); 52 | reader.setLineMapper(new DefaultLineMapper() {{ 53 | setLineTokenizer(new DelimitedLineTokenizer() {{ 54 | setNames(new String[] {"fullname", "code", "email"} ); 55 | }}); 56 | setFieldSetMapper(new BeanWrapperFieldSetMapper(){{ 57 | setTargetType(Student.class); 58 | }}); 59 | }}); 60 | return reader; 61 | } 62 | 63 | @Bean 64 | public StudentItemProcessor processor() { 65 | return new StudentItemProcessor(sender, attachment); 66 | } 67 | 68 | @Bean 69 | public MailBatchItemWriter writer() { 70 | MailBatchItemWriter writer = new MailBatchItemWriter(); 71 | return writer; 72 | } 73 | // end::readerwriterprocessor[] 74 | 75 | // tag::listener[] 76 | 77 | @Bean 78 | public JobExecutionListener listener() { 79 | return new JobCompletionNotificationListener(email); 80 | } 81 | 82 | // end::listener[] 83 | 84 | // tag::jobstep[] 85 | @Bean 86 | public Job importUserJob() { 87 | return jobBuilderFactory.get("importUserJob") 88 | .incrementer(new RunIdIncrementer()) 89 | .listener(listener()) 90 | .flow(step1()) 91 | .end() 92 | .build(); 93 | } 94 | 95 | @Bean 96 | public Step step1() { 97 | return stepBuilderFactory.get("step1") 98 | . chunk(10) 99 | .reader(reader()) 100 | .processor(processor()) 101 | .writer(writer()) 102 | .build(); 103 | } 104 | // end::jobstep[] 105 | } 106 | --------------------------------------------------------------------------------