Showing posts with label quartz. Show all posts
Showing posts with label quartz. Show all posts

Saturday, 11 June 2011

Spring + Quartz + JavaMail Integration Tutorial

Introduction

Quartz is a job scheduling framework which is used to schedule the jobs to be executed on the specified time schedule. Quartz can be downloaded from here. We discussed about quartz earlier - Quartz Example.
JavaMail is an API to send/recieve emails from Java Applications. JavaMail API 1.4.3 can be downloaded from here.
Spring has integration points to integrate Quartz and JavaMail which makes easy to use those APIs.

Example
Lets create a small demo application to show how to integrate Spring + Quartz + JavaMail.

Our application is to send birthday wishes emails to friends everyday at 6 AM.
Lets look at the implementation now.

Email.java
public class Email 
{
private String from;
private String[] to;
private String[] cc;
private String[] bcc;
private String subject;
private String text;
private String mimeType;
private List<Attachment> attachments = new ArrayList<Attachment>();

public String getFrom()
{
return from;
}
public void setFrom(String from)
{
this.from = from;
}
public String[] getTo()
{
return to;
}
public void setTo(String... to)
{
this.to = to;
}
public String[] getCc()
{
return cc;
}
public void setCc(String... cc)
{
this.cc = cc;
}
public String[] getBcc()
{
return bcc;
}
public void setBcc(String... bcc)
{
this.bcc = bcc;
}
public String getSubject()
{
return subject;
}
public void setSubject(String subject)
{
this.subject = subject;
}
public String getText()
{
return text;
}
public void setText(String text)
{
this.text = text;
}
public String getMimeType()
{
return mimeType;
}
public void setMimeType(String mimeType)
{
this.mimeType = mimeType;
}
public List<Attachment> getAttachments()
{
return attachments;
}
public void addAttachments(List<Attachment> attachments)
{
this.attachments.addAll(attachments);
}
public void addAttachment(Attachment attachment)
{
this.attachments.add(attachment);
}
public void removeAttachment(int index)
{
this.attachments.remove(index);
}
public void removeAllAttachments()
{
this.attachments.clear();
}
}

Attachment.java
public class Attachment
{
private byte[] data;
private String filename;
private String mimeType;
private boolean inline;

public Attachment()
{
}

public Attachment(byte[] data, String filename, String mimeType)
{
this.data = data;
this.filename = filename;
this.mimeType = mimeType;
}
public Attachment(byte[] data, String filename, String mimeType, boolean inline)
{
this.data = data;
this.filename = filename;
this.mimeType = mimeType;
this.inline = inline;
}
public byte[] getData()
{
return data;
}
public void setData(byte[] data)
{
this.data = data;
}
public String getFilename()
{
return filename;
}
public void setFilename(String filename)
{
this.filename = filename;
}

public String getMimeType()
{
return mimeType;
}

public void setMimeType(String mimeType)
{
this.mimeType = mimeType;
}

public boolean isInline()
{
return inline;
}

public void setInline(boolean inline)
{
this.inline = inline;
}

}

EmailService.java
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.util.ByteArrayDataSource;

import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;

public class EmailService
{
private JavaMailSenderImpl mailSender = null;
public void setMailSender(JavaMailSenderImpl mailSender)
{
this.mailSender = mailSender;
}

public void sendEmail(Email email) throws MessagingException {
MimeMessage mimeMessage = mailSender.createMimeMessage();
// use the true flag to indicate you need a multipart message
boolean hasAttachments = (email.getAttachments()!=null &&
email.getAttachments().size() > 0 );
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, hasAttachments);
helper.setTo(email.getTo());
helper.setFrom(email.getFrom());
helper.setSubject(email.getSubject());
helper.setText(email.getText(), true);

List<Attachment> attachments = email.getAttachments();
if(attachments != null && attachments.size() > 0)
{
for (Attachment attachment : attachments)
{
String filename = attachment.getFilename() ;
DataSource dataSource = new ByteArrayDataSource(attachment.getData(),
attachment.getMimeType());
if(attachment.isInline())
{
helper.addInline(filename, dataSource);
}else{
helper.addAttachment(filename, dataSource);
}
}
}

mailSender.send(mimeMessage);
}
}

BirthdayWisherJob.java
import javax.mail.MessagingException;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class BirthdayWisherJob extends QuartzJobBean
{

private EmailService emailService;
public void setEmailService(EmailService emailService)
{
this.emailService = emailService;
}

@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException
{
System.out.println("Sending Birthday Wishes... ");
List<User> usersBornToday = getUsersBornToday();
for (User user : usersBornToday)
{
try
{
Email email = new Email();
email.setFrom("xyz@gmail.com.com");
email.setSubject("Happy BirthDay");
email.setTo(user.getEmail());
email.setText("<h1>Dear "+user.getName()+
",
Many Many Happy Returns of the day :-)</h1>");

byte[] data = null;
ClassPathResource img = new ClassPathResource("HBD.gif");
InputStream inputStream = img.getInputStream();
data = new byte[inputStream.available()];
while((inputStream.read(data)!=-1));

Attachment attachment = new Attachment(data, "HappyBirthDay",
"image/gif", true);
email.addAttachment(attachment);

emailService.sendEmail(email);
}
catch (MessagingException e)
{
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

private List<User> getUsersBornToday()
{
List<User> users = new ArrayList<User>();
User user1 = new User("Kinshuk Chandra", "kinshuk.ram.k@gmail.com", new Date());
users.add(user1);
User user2 = new User("John", "abcd@gmail.com", new Date());
users.add(user2);
return users;
}
}

spring config file - applicationContext.xml
<beans>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="birthdayWisherCronTrigger" />
</list>
</property>
</bean>
<bean id="birthdayWisherCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="birthdayWisherJob" />
<!-- run every morning at 6 AM -->
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>

<bean name="birthdayWisherJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.vaani.email.jobs.BirthdayWisherJob" />
<property name="jobDataAsMap">
<map>
<entry key="emailService" value-ref="emailService"></entry>
</map>
</property>
</bean>

<bean id="emailService" class="com.vaani.email.services.EmailService">
<property name="mailSender" ref="mailSender"></property>
</bean>

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="defaultEncoding" value="UTF-8"/>
<property name="host" value="smtp.gmail.com" />
<property name="port" value="465" />
<property name="protocol" value="smtps" />
<property name="username" value="admin@gmail.com"/>
<property name="password" value="*****"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtps.auth">true</prop>
<prop key="mail.smtps.starttls.enable">true</prop>
<prop key="mail.smtps.debug">true</prop>
</props>
</property>
</bean>

</beans>

MailClientDemo.java
package com.vaani.email.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MailClientDemo {


public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}

}


Wednesday, 27 April 2011

Quartz example

 Quartz is a full-featured, open source job scheduling system that can be integrated with, or used along side virtually any Java Enterprise of stand-alone application. The Quartz Scheduler includes many enterprise-class features, such as JTA transactions and clustering. The following is a list of features available:
  • Can run embedded within another free standing application
  • Can be instantiated within an application server (or servlet container).
  • Can participate in XA transactions, via the use of JobStoreCMT.
  • Can run as a stand-alone program (within its own Java Virtual Machine), to be used via RMI
  • Can be instantiated as a cluster of stand-alone programs (with load-balance and fail-over capabilities)
  • Supoprt for Fail-over
  • Support for Load balancing.
Quartz components

  1. Scheduler Task – Pure Java class, the task you want to schedule. 
  2. Scheduler Job – Get the “Scheduler Task” from “JobDetail” via “task name“, and specify which schedule task (method) to run.
  3. Scheduler JobDetail – Define a “task name” and link it with “Scheduler Job”.
  4. Trigger – When will run your “Scheduler JobDetail”.
  5. Scheduler – Link “JobDetail” and “Trigger” together and schedule it.
Understanding the whole sequence by code.

Prequisities of coding
The following example demonstrates the use of Quartz scheduler from a stand-alone application. Follow these steps to setup the example, in Eclipse.
  1. Download the latest version of quartz from opensymphony.
  2. Make sure you have the following in your class path (project-properties->java build path):
    • The quartz jar file (quartz-1.7.0.jar).
    • Commons logging (commons-logging-1.1.1.jar)
    • Add any server runtime to your classpath in eclipse. This is for including the Java transaction API used by Quartz. Alternatively, you can include the JTA class files in your classpath as follows
      1. Download the JTA classes zip file from the JTA download page.
      2. Extract the files in the zip file to a subdirectory of your project in Eclipse.
      3. Add the directory to your Java Build Path in the project->preferences, as a class directory.

The code components:

1. Scheduler Task

Create a pure Java class, this is the class you want to schedule.
package com.vaani.common;
 
public class RunMeTask
{
public void printMe() {
System.out.println("Run Me ~");
}
}

2. Scheduler Job

Create a ‘job’ to implement the Quartz Job interface, and also the execute() method. Get the scheduler task from Job Details via “task name”, and specify which schedule task (method) to run.

package com.vaani.common;
 
import java.util.Map;
 
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
public class RunMeJob implements Job
{
public void execute(JobExecutionContext context)
throws JobExecutionException {
 
Map dataMap = context.getJobDetail().getJobDataMap();
RunMeTask task = (RunMeTask)dataMap.get("runMeTask");
task.printMe();
}
}
 
or  

//you can use already defined scheduler tasks like calendar etc..any POJO
public class RunMeJob implements Job
{
public void execute(JobExecutionContext context)
throws JobExecutionException {
 
System.out.println("Executing job at: " + 
Calendar.getInstance().getTime()
+ " triggered by: " + context.getTrigger().getName());
}}

3. Scheduler JobDetail

Initialize a JobDetail object, link your scheduler job with setJobClass(RunMeJob.class); method, define a task name and put it into Job data map, dataMap.put(“runMeTask”, task);. This “task name” is the only link between your Job and JobDetail.
P.S : job.setName(“runMeJob”) has no special function, just a descriptive name, you can put anything.
RunMeTask task = new RunMeTask();
 
//specify your sceduler task details
JobDetail job = new JobDetail();
job.setName("runMeJob");
job.setJobClass(RunMeJob.class);
 
Map dataMap = job.getJobDataMap();
dataMap.put("runMeTask", task);

4. Quartz Triggers

Quartz triggers are used to define when the Quartz will run your declared scheduler job. There are two types of Quartz triggers :
  • SimpleTrigger – allows to set start time, end time, repeat interval to run yout job.
  • CronTrigger – allows Unix cron expression to specify the dates and times to run your job.
Code snippet – SimpleTrigger
SimpleTrigger trigger = new SimpleTrigger();
trigger.setName("runMeJobTesting");
trigger.setStartTime(new Date(System.currentTimeMillis() + 1000));
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(30000);
Code snippet – CronTrigger
//configure the scheduler time
CronTrigger trigger = new CronTrigger();
trigger.setName("runMeJobTesting");
trigger.setCronExpression("0/30 * * * * ?");

5. Scheduler

Link the “JobDetail” and “Trigger” together and schedule it.
//schedule it
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);


Sample code

SimpleJob:
public class SimpleJob implements Job {
public void execute(JobExecutionContext context) 
throws JobExecutionException {
System.out.println("Executing job at: " + Calendar.getInstance().getTime()
+ " triggered by: " + context.getTrigger().getName());
}
}


Scheduling job with runner:
public class QuartzTest {
public static void main(String[] args) {
try {
// Get a scheduler instance.
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

long ctime = System.currentTimeMillis();

// Create a trigger.
JobDetail jobDetail = new JobDetail("Job Detail", 
"jGroup", SimpleJob.class);
SimpleTrigger simpleTrigger = new SimpleTrigger("My Trigger", "tGroup");
simpleTrigger.setStartTime(new Date(ctime));

// Set the time interval and number of repeats.
simpleTrigger.setRepeatInterval(100);
simpleTrigger.setRepeatCount(10);

// Add trigger and job to Scheduler.
scheduler.scheduleJob(jobDetail, simpleTrigger);

// Start the job.
scheduler.start();
} catch (SchedulerException ex) {
ex.printStackTrace();
}
}
}



Output
Apr 27, 2011 5:20:47 PM org.quartz.simpl.SimpleThreadPool initialize
INFO: Job execution threads will use class loader of thread: main
Apr 27, 2011 5:20:47 PM org.quartz.core.SchedulerSignalerImpl <init>
INFO: Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
Apr 27, 2011 5:20:47 PM org.quartz.core.QuartzScheduler <init>
INFO: Quartz Scheduler v.1.7.3 created.
Apr 27, 2011 5:20:47 PM org.quartz.simpl.RAMJobStore initialize
INFO: RAMJobStore initialized.
Apr 27, 2011 5:20:47 PM org.quartz.impl.StdSchedulerFactory instantiate
INFO: Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
Apr 27, 2011 5:20:47 PM org.quartz.impl.StdSchedulerFactory instantiate
INFO: Quartz scheduler version: 1.7.3
Apr 27, 2011 5:20:47 PM org.quartz.core.QuartzScheduler start
INFO: Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
Executing at: Wed Apr 27 17:20:47 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:47 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:47 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger
Executing at: Wed Apr 27 17:20:48 IST 2011 triggered by: My Trigger

References
The Unix cron expression is highly flexible and powerful, you can learn and see many advance cron expression examples in following website.
1. http://en.wikipedia.org/wiki/CRON_expression
2. http://www.quartz-scheduler.org/docs/examples/Example3.html