Monday, May 10, 2010

Write a Scheduler using Quartz

A lot of times, as a developer, we need to run a task in a scheduled manner. In Linux/UNIX, we have cron job. In Windows, we have Scheduled Task. But if let's say we work in an environment where we have no luxury to use any of them, as a Java developer, we can easily write a scheduler using Quartz.

Quartz has two main components, Trigger and Job. To use cron-like trigger, we can use org.quartz.CronTrigger.

MyTrigger.java
package myproject;

import org.quartz.CronExpression;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;

public class MyTrigger {

    public void task(String cronExpression, String[] commands) throws SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();

        JobDetail jobDetail = new JobDetail("myJobDetail", "myJobDetailGroup",
                MyJob.class);
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        jobDataMap.put("commands", commands);
        jobDetail.setJobDataMap(jobDataMap);

        CronTrigger cronTrigger = new CronTrigger("myTrigger", "myTriggerGroup");

        try {
            CronExpression cexp = new CronExpression(cronExpression);
            cronTrigger.setCronExpression(cexp);
        } catch (Exception e) {
            e.printStackTrace();
        }
        scheduler.scheduleJob(jobDetail, cronTrigger);

        scheduler.start();
    }

    public static void main(String[] args) {
        if (!validateArgs(args)) {
            printUsage();
            System.exit(1);
        }

        String cronExpression = args[0];
        String[] commands = new String[args.length-1];
        for (int i = 0; i < args.length-1; i++) {
            commands[i] = args[i+1];
        }

        try {
            MyTrigger trigger = new MyTrigger();
            trigger.task(cronExpression, commands);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static boolean validateArgs(String[] args) {
        if (args.length < 2) {
            return false;
        }
        return true;
    }

    private static void printUsage() {
        System.out.println("Usage: java MyTrigger [cron_exp] [command1] [command2] ...");
    }
}
The jobDataMap in the code above is used to pass the data from the Trigger to a Job. In this example, the data being passed is a list of commands to be executed. MyJob.java
package myproject;

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class MyJob implements Job {

    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("MyJob - executing its job at "
                + new Date() + " by " + context.getTrigger().getName());
        String[] commands = (String[]) context.getMergedJobDataMap().get("commands");
        ProcessBuilder pb = new ProcessBuilder(commands);
        try {
            Process p = pb.start();
            StreamGobbler outputStreamGobbler = new StreamGobbler(p.getInputStream());
            StreamGobbler errorStreamGobbler = new StreamGobbler(p.getErrorStream());

            outputStreamGobbler.start();
            errorStreamGobbler.start();

            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
To retrieve the jobDataMap from the Trigger, we can call context.getMergedJobDataMap(). StreamGobbler.java
package myproject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class StreamGobbler extends Thread {

    private InputStream is;

    public StreamGobbler(InputStream is) {
        this.is = is;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(is));
            String line = "";
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1 comment: