Monday, December 26, 2011

Automate Email(gmail) with Spring, Quartz and Java

The tool I will describe here is automated email sending application that can be scheduled in various ways. It doesn't need to run on a server but you have the option for it.

I will include all end to end code, including the build instruction, setting up the libraries with maven and using it with various options.

Look for "Download" link for the zip file with all the projected files at the bottom of this blog (before my signature).
The link should take you to a free space hosting site. I have included complete source code with maven script, java source code and spring and quartz configuration files and the properties file.

Software framework used in this email scheduling application are:

Spring
Maven
Java 6 (java mail api)
QuartzScheduler
Spring-Quartz support

I have used Eclipse (J2EE SOA) to develop this application.

I will start with the application context file that I have used to tell spring what to load and what to inject.

1:    
2:  <?xml version="1.0" encoding="UTF-8"?>  
3:    
4:  <!--   
5:   Copyright 2011: Nirmal Singh Software Resource and Services.  
6:   Disclosure, Use or Reproduction without the written authorization of NSSRS is "not" is prohibited.  
7:  -->  
8:    
9:  <beans xmlns="http://www.springframework.org/schema/beans"  
10:       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
11:       xmlns:aop="http://www.springframework.org/schema/aop"  
12:       xmlns:tx="http://www.springframework.org/schema/tx"  
13:       xmlns:context="http://www.springframework.org/schema/context"  
14:       xsi:schemaLocation="http://www.springframework.org/schema/beans   
15:        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
16:        http://www.springframework.org/schema/aop   
17:        http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
18:        http://www.springframework.org/schema/tx   
19:        http://www.springframework.org/schema/tx/spring-tx-2.0.xsd  
20:              http://www.springframework.org/schema/context   
21:              http://www.springframework.org/schema/context/spring-context.xsd">  
22:      
23:       <!-- the parent application context definition for the dashboard application -->  
24:         
25:    <bean id="propertyConfigurer"   
26:       class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
27:      <property name="locations">  
28:        <list>  
29:          <value>classpath:notification.properties</value>  
30:        </list>  
31:      </property>  
32:    </bean>  
33:         
34:       <bean id="emailerImpl" class="org.springframework.mail.javamail.JavaMailSenderImpl">  
35:      <property name="host" value="${mail.host}"/>  
36:      <property name="port" value="${mail.port}"/>  
37:      <property name="username" value="${mail.username}"/>  
38:      <property name="password" value="${mail.password}"/>  
39:      <property name="javaMailProperties">  
40:        <props>  
41:          <!-- Use SMTP transport protocol -->  
42:          <prop key="mail.transport.protocol">smtp</prop>  
43:          <!-- Use SMTP-AUTH to authenticate to SMTP server -->  
44:          <prop key="mail.smtp.auth">true</prop>  
45:          <!-- Use TLS to encrypt communication with SMTP server -->  
46:          <prop key="mail.smtp.starttls.enable">true</prop>  
47:          <prop key="mail.debug">true</prop>  
48:           <prop key="mail.smtp.quitwait">false</prop>  
49:        </props>  
50:      </property>  
51:    </bean>  
52:         
53:         
54:       <bean id="emailManager" class="com.nirmal.singh.spring.gmail.SimpleMailManager">  
55:            <property name="sender" ref="emailerImpl"/>  
56:            <property name="senderAddress" value="${mail.from}"/>  
57:            <property name="notificationGroup" value="${mail.to}"/>  
58:       </bean>  
59:         
60:  </beans>  
61:    


Lines 34 and 54 are crucial here. As you can see, the application is going to use org.springframework.mail.javamail.JavaMailSenderImpl and that object can intern be used by your(mine) custom written SimpleMailManager(implements custom written MailManager)

Here are SimpleMailManager and MailManager:

SimpleMailManager.java:
1:    
2:  package com.nirmal.singh.spring.gmail;  
3:    
4:  import org.springframework.mail.MailSender;  
5:  import org.springframework.mail.SimpleMailMessage;  
6:    
7:  import com.nirmal.singh.spring.gmail.mailmanager.MailManager;  
8:    
9:  public class SimpleMailManager implements MailManager {  
10:    
11:       public MailSender sender = null;  
12:    
13:       public String senderAddress;  
14:    
15:       public String[] notificationGroup;  
16:    
17:       public void setSender(MailSender sender){  
18:            this.sender = sender;  
19:       }  
20:    
21:       public void setSenderAddress(String senderAddress){  
22:            this.senderAddress = senderAddress;  
23:       }  
24:    
25:       public void setNotificationGroup(String delimitedAddressString){  
26:            if (delimitedAddressString != null && delimitedAddressString.trim().length() > 0){  
27:                 String[] possibleAddress = delimitedAddressString.split(";");  
28:                 int nonEmptyCnt = 0;  
29:                 for (String str : possibleAddress){  
30:                      if (str != null && str.trim().length() > 0){  
31:                           nonEmptyCnt++;  
32:                      }  
33:                 }  
34:                 if (nonEmptyCnt > 0){  
35:                      String[] nonEmpty = new String[nonEmptyCnt];  
36:                      int ind = 0;  
37:                      for (String str : possibleAddress){  
38:                           if (str != null && str.trim().length() > 0){  
39:                                nonEmpty[ind] = str.trim();  
40:                                ind++;  
41:                           }  
42:                      }  
43:                      this.notificationGroup = nonEmpty;  
44:                 }  
45:            }  
46:       }  
47:    
48:       public void process(SimpleMailMessage mail){  
49:            if (emptyString(mail.getFrom())){  
50:                 mail.setFrom(this.senderAddress);  
51:            }  
52:            if (mail.getTo() == null || mail.getTo().length < 1){  
53:                 mail.setTo(this.notificationGroup);  
54:            }  
55:              
56:            this.sender.send(mail);  
57:       }  
58:    
59:       public SimpleMailMessage getTemplateNotification(){  
60:            SimpleMailMessage message = new SimpleMailMessage();  
61:            message.setFrom(this.senderAddress);  
62:            message.setReplyTo(this.senderAddress);  
63:            message.setTo(this.notificationGroup);  
64:            return message;  
65:       }  
66:    
67:       private boolean emptyString(String str){  
68:            return str == null || str.trim().length() < 1;  
69:       }  
70:    
71:  }  
72:    
73:    

MailManager.java:

1:    
2:  package com.nirmal.singh.spring.gmail.mailmanager;  
3:  import org.springframework.mail.SimpleMailMessage;  
4:    
5:  public interface MailManager {  
6:       /**  
7:        * Gets an email instance with the typical addressing information for a general notification.  
8:        * @return An email object with the configured values for addresses.  
9:        */  
10:       public SimpleMailMessage getTemplateNotification();  
11:    
12:       public void process(SimpleMailMessage mail);  
13:    
14:  }  
15:    

Now, the best way to take care of all the dependencies, as most open source developers can agree, is to use maven dependencies for your project. Here is all in one pom.xml for this application.

pom.xml
1:    
2:  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
4:   <modelVersion>4.0.0</modelVersion>  
5:   <groupId>com.nirmal.singh.spring.sendemail</groupId>  
6:   <artifactId>SpringQuartzGmailMaven</artifactId>  
7:   <packaging>jar</packaging>  
8:   <version>1.0-SNAPSHOT</version>  
9:   <name>SpringExample</name>  
10:   <url>http://maven.apache.org</url>  
11:     
12:   <dependencies>  
13:    <!-- Spring framework -->  
14:       <dependency>  
15:            <groupId>org.springframework</groupId>  
16:            <artifactId>spring</artifactId>  
17:            <version>2.5.6</version>  
18:       </dependency>  
19:      
20:    <!-- Quartz framework -->  
21:    <dependency>  
22:         <groupId>opensymphony</groupId>  
23:         <artifactId>quartz</artifactId>  
24:         <version>1.6.3</version>  
25:       </dependency>  
26:    
27:       <dependency>  
28:         <groupId>commons-collections</groupId>  
29:         <artifactId>commons-collections</artifactId>  
30:         <version>3.2.1</version>  
31:       </dependency>  
32:    
33:       <!-- java activation framework -->       
34:       <dependency>  
35:            <groupId>javax.activation</groupId>  
36:            <artifactId>activation</artifactId>  
37:            <version>1.1</version>  
38:       </dependency>  
39:         
40:       <!-- java mail framework -->  
41:       <dependency>  
42:            <groupId>javax.mail</groupId>  
43:            <artifactId>mail</artifactId>  
44:            <version>1.4</version>  
45:       </dependency>  
46:         
47:      
48:   </dependencies>  
49:  </project>  
50:    

Since there are a lot of things that can change for email preferences, I have included those in a properties file.

notification.properties:
1:    
2:  # Properties file with mail-related settings, used for notification emails.  
3:  # Applied by PropertyPlaceholderConfigurer from "applicationContext.xml".  
4:  # Targeted at system administrators, to avoid touching the context XML files.  
5:  #copyright: Nirmal Singh Software Resource Services nirmalksingh@gmail.com  
6:    
7:  # SMTP host used as email relay  
8:  mail.host=smtp.gmail.com  
9:  mail.port=25  
10:  # Address appearing as "From" for template-based messages  
11:  mail.from=your_email_id@gmail.com  
12:    
13:  # Address(e) appearing as "To" for template-based messages  
14:  # Multiple addresses maybe separated by semi-colons (;)  
15:  #you can send email to as many as I guess 1000 or even more. Please check on that.  
16:  mail.to=some_address1@hotmail.com;some_address1@students.someuniv.edu;some_other@some_site.something  
17:    
18:  # google gmail requires your sender's email address for sending email from non-gmail application like this application  
19:  mail.username=your_gmail_address (eg. nirmalksingh@gmail.com)  
20:    
21:  # google gmail requires your sender's password for sending email from non-gmail application like this application  
22:  mail.password=your_password  
23:    


Now it is turn to talk about the Quartz Scheduler.
First of all I'll share with you the quartz scheduler configuration file.

Spring-Quartz.xml: ( we will load this file simultaneously when we load application contecxt file)
1:  <beans xmlns="http://www.springframework.org/schema/beans"  
2:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:  xsi:schemaLocation="http://www.springframework.org/schema/beans  
4:  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  
5:     
6:    <bean id="sendEmailTask" class="com.nirmal.singh.spring.quartz.task.SendEmailTask" />  
7:      
8:    <bean name="jobDetailBean" class="org.springframework.scheduling.quartz.JobDetailBean">  
9:            <property name="jobClass" value="com.nirmal.singh.spring.quartz.job.RunJobSendEmailTask" />  
10:            <property name="jobDataAsMap">  
11:                 <map>  
12:                      <entry key="sendEmailTask" value-ref="sendEmailTask" />  
13:                 </map>  
14:            </property>  
15:       </bean>  
16:     
17:       <bean id="methodInvokingJobDetailFactoryBean" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
18:            <property name="targetObject" ref="sendEmailTask"/>  
19:            <property name="targetMethod" value="runSendEmailTask"/>  
20:            <!--If JobDetail objects implement the Stateful interface, this won't happen.   
21:            The second job will not start before the first one has finished.   
22:            To make jobs resulting from the MethodInvokingJobDetailFactoryBean non-concurrent,   
23:            set the concurrent flag to false. as in below-->  
24:            <property name="concurrent" value="false"/>  
25:       </bean>  
26:     
27:        <!-- experimental -->  
28:       <!-- Simple Trigger -->  
29:       <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">  
30:             <!-- see the example of method invoking job above -->  
31:             <property name="jobDetail" ref="jobDetailBean"/>  
32:            <!-- 10 seconds -->  
33:             <property name="startDelay" value="10000"/>  
34:            <!-- repeat every 50 seconds -->  
35:  <!--           <property name="repeatInterval" value="50000"/>-->  
36:             <property name="repeatInterval" value="15000"/>  
37:       </bean>  
38:         
39:       <!-- Cron Trigger -->  
40:  <!--     <bean id="cronTrigger"-->  
41:  <!--          class="org.springframework.scheduling.quartz.CronTriggerBean">-->  
42:  <!--          <property name="jobDetail" ref="methodInvokingJobDetailFactoryBean" />-->  
43:  <!--          <property name="cronExpression" value="1 39-41 16 * * ?" /> Fire every minute starting at 4:06PM(inclusive) and ending at 4:09 (inclusive) PM, every day-->  
44:  <!--     </bean>-->  
45:    
46:       <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
47:            <property name="jobDetails">  
48:                 <list>  
49:                      <ref bean="jobDetailBean" />  
50:                 </list>  
51:            </property>  
52:            <property name="triggers">  
53:                 <list>  
54:                      <ref bean="simpleTrigger" /> <!-- experimental -->  
55:  <!--                    <ref bean="cronTrigger" />-->  
56:                 </list>  
57:            </property>  
58:       </bean>  
59:     
60:  </beans>  

That's it configuration-wise. Here are the java resources that are used by the above quartz scheduler configurer.

QuartzGmailMainApp.java is the starting of the application, you need to run this one to make the application work and send emails, be sure to use your preferred cron expression in the Spring-Quartz.xml file. Here are some examples of cronExpressions:


0 0 12 * * ? Fire at 12:00 PM (noon) every day
0 15 10 ? * * Fire at 10:15 AM every day
0 15 10 * * ? Fire at 10:15 AM every day
0 15 10 * * ? * Fire at 10:15 AM every day
0 15 10 * * ? 2005 Fire at 10:15 AM every day during the year 2005
0 * 14 * * ? Fire every minute starting at 2:00 PM and ending at 2:59 PM, every day
0 0/5 14 * * ? Fire every 5 minutes starting at 2:00 PM and ending at 2:55 PM, every day
0 0/5 14,18 * * ? Fire every 5 minutes starting at 2:00 PM and ending at 2:55 PM, AND fire every 5 minutes starting at 6:00 PM and ending at 6:55 PM, every day
0 0-5 14 * * ? Fire every minute starting at 2:00 PM and ending at 2:05 PM, every day
0 10,44 14 ? 3 WED Fire at 2:10 PM and at 2:44 PM every Wednesday in the month of March
0 15 10 ? * MON-FRI Fire at 10:15 AM every Monday, Tuesday, Wednesday, Thursday and Friday
0 15 10 15 * ? Fire at 10:15 AM on the 15th day of every month
0 15 10 L * ? Fire at 10:15 AM on the last day of every month
0 15 10 ? * 6L Fire at 10:15 AM on the last Friday of every month
0 15 10 ? * 6L Fire at 10:15 AM on the last Friday of every month
0 15 10 ? * 6L 2002-2005 Fire at 10:15 AM on every last friday of every month during the years 2002, 2003, 2004, and 2005
0 15 10 ? * 6#3 Fire at 10:15 AM on the third Friday of every month
0 0 12 1/5 * ? Fire at 12 PM (noon) every 5 days every month, starting on the first day of the month
0 11 11 11 11 ? Fire every November 11 at 11:11 AM




Here is the main class of the application:
QuartzGmailMainApp.java:

1:    
2:  package com.nirmal.singh.spring.quartz.gmail.mainapp;  
3:    
4:  import com.nirmal.singh.spring.quartz.task.SendEmailTask;  
5:    
6:  import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;  
7:  import org.springframework.context.support.GenericApplicationContext;  
8:  import org.springframework.core.io.ClassPathResource;  
9:    
10:  public class QuartzGmailMainApp {  
11:    public static void main( String[] args ) throws Exception {  
12:         GenericApplicationContext context = new GenericApplicationContext();  
13:         XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(context);  
14:            xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext-gmail.xml"));  
15:            xmlReader.loadBeanDefinitions(new ClassPathResource("Spring-Quartz.xml"));  
16:            context.refresh();  
17:         SendEmailTask.context = context;  
18:    }  
19:  }  
20:    

SendEmailTask.java (contains task invoked by the quartz scheduler)
1:  package com.nirmal.singh.spring.quartz.task;  
2:    
3:  import org.springframework.context.support.GenericApplicationContext;  
4:  import org.springframework.mail.SimpleMailMessage;  
5:    
6:  import com.nirmal.singh.spring.gmail.SimpleMailManager;  
7:    
8:  public class SendEmailTask {  
9:         
10:       static int i = 0;  
11:       public static GenericApplicationContext context;  
12:    
13:       /**  
14:        * This job is configured to trigger with a qurtz scheduler's cron expression in Spring-Quartz.xml file.   
15:        */  
16:       public void runSendEmailTask() {  
17:    
18:            System.out.println("Running job #" + ++i);  
19:    
20:            SimpleMailManager mailMgr = (SimpleMailManager) context.getBean("emailManager");  
21:            if (mailMgr.notificationGroup != null && mailMgr.notificationGroup.length > 0) {  
22:                 String[] str = (String[]) mailMgr.notificationGroup;  
23:                 System.out.println("Notified group email address(s) :");  
24:                 for (int i = 0; i < str.length; i++) {  
25:                      System.out.println(str[i]);  
26:                 }  
27:            }  
28:    
29:            System.out.println("Sender's address :");  
30:            System.out.println(mailMgr.senderAddress.toString());  
31:    
32:            SimpleMailMessage mail = mailMgr.getTemplateNotification();  
33:    
34:            StringBuffer sb = new StringBuffer();  
35:            sb.append("This is a test email from Nirmal, please ignore.");  
36:            mail.setText(sb.toString());  
37:            mail.setSubject("This is a test message, please ignore.");  
38:            mailMgr.process(mail);  
39:       }  
40:  }  

Quartz scheduler uses a "job" class to internally tell which user's task and target method for that task is to be used. As noted earlier in the Spring-Quartz.xml file. Here is the RunJobSendEmailTask.java
1:  package com.nirmal.singh.spring.quartz.job;  
2:    
3:  import org.quartz.JobExecutionContext;  
4:  import org.quartz.JobExecutionException;  
5:  import org.springframework.scheduling.quartz.QuartzJobBean;  
6:    
7:  import com.nirmal.singh.spring.quartz.task.SendEmailTask;  
8:     
9:  public class RunJobSendEmailTask extends QuartzJobBean {  
10:       private SendEmailTask sendEmailTask;  
11:     
12:       public void setSendEmailTask(SendEmailTask emailTask) {  
13:            this.sendEmailTask = emailTask;  
14:       }  
15:     
16:       protected void executeInternal(JobExecutionContext context)  
17:       throws JobExecutionException {  
18:            sendEmailTask.runSendEmailTask();  
19:       }       
20:  }  

That's all! Now you can run this application, give it a list of email address and tell the quartz scheduler when you want those email sent out.

Please feel free to ask any question you may have. I have built this application from scratch, I can explain each and every detail to you if you need me to. Please contact for fair pricing and commercial use of this software.
You can email me personally for the same at: nirmalksingh@gmail.com. Thanks for reading!

Link for downloading all the files: Download allFilesForBlogspot.zip

~Nirmal


Democracy means government by discussion, but it is only effective if you can stop people talking.
--Clement Atlee