Using Calendar in Quartz Scheduler for Job fire skip

Quartz Calendars can be used by the scheduler to block of a list of days, range of time or particular days of the year/month/week from the scheduler fire timings. Attaching a calendar onto a trigger ensures that the trigger does not get fired on date/time as defined by the Calendar.

There are different types of Calendar already available or a new Calendar can be using the Quartz calendar interface. List of available calendars on quartz can be got here

The below sample shows the use of one such Calendar – WeeklyCalendar that disables job fire on weekends – perfect for our AlarmScheduler application. The method of using it is to first create an object of the WeeklyCalendar and then add it onto the scheduler along with a string name through which it can be further referenced. Then, this string name is used as an argument in setting the calendar name for the trigger.

import java.util.Calendar;
import java.util.Date;
import java.util.Properties;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.WeeklyCalendar;

import static org.quartz.CronScheduleBuilder.dailyAtHourAndMinute;
import static org.quartz.JobBuilder.newJob;

public class AlarmSchedule {

	public AlarmSchedule(){
		try{
			//Create the weekly calendar object - This by default handles disaabling job fire on weekends so no need
			//to explicitly set
			WeeklyCalendar weeklyOff = new WeeklyCalendar();
			//example of adding an excluded day of the week - This excludes fridays from job firing schedule
			//weeklyOff.setDayExcluded(Calendar.FRIDAY, true);
			SchedulerFactory schdFact = new StdSchedulerFactory();
			Scheduler schd = schdFact.getScheduler();
			//add the Calendar object created to the scheduler with a string identifier to it
			schd.addCalendar("weeklyOff", weeklyOff, false, true);
			schd.start();
			JobDetail job = newJob(AlarmJob.class).withIdentity("alarmjob", "alarmjobgroup").build();
			Trigger trigger = TriggerBuilder.newTrigger()
					.withIdentity("alarmtrigger", "alarmtriggergroup")
					.startNow()
					.withSchedule(dailyAtHourAndMinute(6, 30))
					.modifiedByCalendar("weeklyOff")
					.build();
			schd.scheduleJob(job,trigger);
		}
		catch(SchedulerException e){
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		new AlarmSchedule();
	}
}

AlarmJob.java –

import java.util.Date;

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

public class AlarmJob implements Job {

	@Override
	public void execute(JobExecutionContext arg0) throws JobExecutionException {
		System.out.println("WAKE UP CALL "+new Date());
	}

}

Code @ Git – https://github.com/vageeshhoskere/blog

JUnit Test case for Java Classes

Junit is a framework that can be used to perform unit test cases on the classes written. Junit-4.0 provides for an option for the Test cases to be used along with the class by including the annotation @Test which tells the framework to run it as a test case.

Tests are run using assertions on whether the actual and expected output matches or not. Below is a sample program (JUnitSample.java) which has two methods add and divide, both taking two integer arguments and return integer result. This program also includes a test method addTest which asserts on the method add ( test case used within the class itself). The other class is the JUnit test case class (JunitSampleTest.java) which is the junit class for the former.

JunitSample.java –


package sample;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class JunitSample {

	public JunitSample(){
		
	}
	
	public int add(int a , int b){
		return(a+b);
	}
	
	public int divide(int a , int b){
		try{
			/**
			 * if b is zero there is divide by zero exception and the method returns
			 * the value of a itself
			 */
			a = a/b;
		}
		catch(Exception e){
			e.printStackTrace();
		}
		return a;
	}
	/*
	 * JUnit test method which asserts on whether the method add is adding the two numbers
	 * 3 and 2 correctly or not
	 */
	@Test
	public void addTest(){
		assertEquals(5, new JunitSample().add(3, 2));
	}
	
	public static void main(String[] args) {
		new JunitSample();
	}

} 

JunitSampleTest.java –

package sample;

import static org.junit.Assert.*;

import org.junit.Test;

public class JunitSampleTest {
	
	@Test
	public void addTest(){
		assertEquals(5, new JunitSample().add(3, 2));
	}
	
	@Test
	public void divideTest(){
		assertEquals(1, new JunitSample().divide(3, 2));
	}
	
	@Test
	public void divideErrorTest(){
		/**
		 * This methods asserts that the method divide on passing 0 as second argument 
		 * returns the first argument as the method throws division by 0 exception as 
		 * defined by JUnitSample.divide()
		 */
		assertEquals(3,new JunitSample().divide(3, 0));
	}
	
}

XML Parsing using JAXB

Jaxb can be used for reading xml file and storing it as a java object. It uses the binding classes to bind a schema definition file with the java class objects. Generating the binding classes can be done by installing JAXB and then running the command xjc.bat for windows or xjc.sh for linux. The sample program below demonstrates reading a xml file

The command to generate binding classes using xjc would be –

<JAXB_INSTALL_LOCATION>/bin/xjc.bat <Schema file>

Sample.xml

<?xml version="1.0" encoding="UTF-8"?>
<JavaCollectionUtils>
	<Type name="List" >
		<impl name="arraylist"/>
		<impl name="linkedlist"/>
	</Type>
	<Type name="map">
		<impl name="HashMap"/>
	</Type>
	<Type name="Table">
		<impl name="HashTable"/>
	</Type>
</JavaCollectionUtils>

Sample.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="JavaCollectionUtils" type="JavaCollectionUtilsType"/>
  <xs:complexType name="implType">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute type="xs:string" name="name" use="optional"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="TypeType">
    <xs:sequence>
      <xs:element type="implType" name="impl" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="name" use="optional"/>
  </xs:complexType>
  <xs:complexType name="JavaCollectionUtilsType">
    <xs:sequence>
      <xs:element type="TypeType" name="Type" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

import java.io.File;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import jaxbBind.ImplType;
import jaxbBind.JavaCollectionUtilsType;
import jaxbBind.TypeType;


public class JaxbTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try{
			//jaxbBind is the package that contains all the jaxb bind classes
			JAXBContext context = JAXBContext.newInstance("jaxbBind");
			//Create an unmarshaller instance to convert xml to java object
			Unmarshaller unmarsh = context.createUnmarshaller();
			SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			//specify the schema definition for parsing
			Schema schema = sf.newSchema(new File("sample.xsd"));
			unmarsh.setSchema(schema);
			//unmarshall the xml file
			JAXBElement<JavaCollectionUtilsType> obj = (JAXBElement<JavaCollectionUtilsType>) unmarsh.unmarshal(new File("sample.xml"));
			JavaCollectionUtilsType collectionUtils = (JavaCollectionUtilsType) obj.getValue();
			//get a list of all tags of type `Type`
			List<TypeType> collectionTypes = collectionUtils.getType();
			for(TypeType collectionType: collectionTypes){
				System.out.println(collectionType.getName());
				//get a list of all tags of type `impl` for a particular Type
				List<ImplType> implTypes = collectionType.getImpl();
				for(ImplType implType : implTypes){
					System.out.println(implType.getName());
				}
			}
		}
		catch(Exception e){
			//The program throws exception if the xml does not confirm to the shema defined
			e.printStackTrace();
		}
		
	}

}

Rescheduling Job from JDBCJobStore in Quartz Scheduler

The last post mentioned about using JDBCJobStore to store Quartz related information so as to ensure that job related details are available permanently and the job can be rescheduled in case the system experiences some outage and downtime.

The sample program below uses the stored job (database) from the previous post and reschedule the PrintStatefulJob

Please note that this program too uses the properties file which has the same set of properties as the previous program.

Sample Program – file quartz.properties

org.quartz.scheduler.instanceName = PRINT_SCHEDULER1
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 4
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

#specify the jobstore used
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties = false

#The datasource for the jobstore that is to be used
org.quartz.jobStore.dataSource = myDS

#quartz table prefixes in the database
org.quartz.jobStore.tablePrefix = qrtz_
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.isClustered = true
org.quartz.scheduler.instanceId = PRINT_SCHEDULER1

#The details of the datasource specified previously
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3307/blog_test
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root
org.quartz.dataSource.myDS.maxConnections = 20
 

Sample program PrintRescheduler.java

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

public class PrintRescheduler {

	private Scheduler scheduler;
	public PrintRescheduler() {
		try {
			scheduler = new StdSchedulerFactory().getScheduler();
			scheduler.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void reSchedule() throws SchedulerException {
		String triggerName = "printTrigger";
		String triggerGroup = "printtriggergroup";
		Trigger oldTrigger = scheduler.getTrigger(TriggerKey.triggerKey(triggerName, triggerGroup));
		//use the same trigger builder so that we do not have to worry about change in name/group
		TriggerBuilder triggerBuilder = oldTrigger.getTriggerBuilder();
		Trigger newTrigger = triggerBuilder
				.withSchedule(simpleSchedule()
						.withIntervalInMilliseconds(200l)
						.repeatForever())
				.build();
		scheduler.rescheduleJob(TriggerKey.triggerKey(triggerName, triggerGroup),newTrigger);
	}

	public void stopScheduler() throws SchedulerException {
		scheduler.shutdown();
	}

	public static void main(String[] args) {
		PrintRescheduler printRescheduler = new PrintRescheduler();
		try {
			Thread.sleep(10000l);
			printRescheduler.reSchedule();
			Thread.sleep(10000l);
			printRescheduler.stopScheduler();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

 

Code @ Github – https://github.com/vageeshhoskere/blog/tree/master/quartz

JDBCJobStore in Quartz Scheduler

Note: Updated the post to reflect code from Quartz version 2.2.*

As mentioned in the previous post, a Job Store is used by the quartz scheduler to store information about itself, and a JDBCJobStore is a way of maintaining the quartz job details over a database via the JDBC. While the use of RAMJobStore indicates volatile storage of the quartz job details, a JDBCJobStore ensures that the information on the quartz jobs, triggers, calendars etc are available any time in case the system has a downtime and then can be rescheduled once the system is up.

A JDBCJobStore requires some database tables to be present in the data source defined for use of quartz. The sql queries for creating and populating the required tables is available under the docs/dbTables folder of the quartz distribution. The following example uses the MySQL database to schedule a job using the JDBCJobStore.

The file quartz.properties is used by the program to define the quartz properties –
Sample Program – file quartz.properties

org.quartz.scheduler.instanceName = PRINT_SCHEDULER
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 4
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

#specify the jobstore used
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties = false

#The datasource for the jobstore that is to be used
org.quartz.jobStore.dataSource = myDS

#quartz table prefixes in the database
org.quartz.jobStore.tablePrefix = qrtz_
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.isClustered = true
org.quartz.scheduler.instanceId = PRINT_SCHEDULER

#The details of the datasource specified previously
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3307/blog_test
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root
org.quartz.dataSource.myDS.maxConnections = 20

Sample program PrintScheduler.java

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import ramstore.PrintStatefulJob;

import java.io.*;
import java.util.Properties;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

public class PrintScheduler {

   private Scheduler scheduler;
   public PrintScheduler(String instanceId) {
      try {
         scheduler = new StdSchedulerFactory().getScheduler();
         scheduler.start();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   public void schedule() throws SchedulerException {
      JobDetail job = newJob(PrintStatefulJob.class).withIdentity("printjob", "printjobgroup").build();
      job.getJobDataMap().put("count",0);
      Trigger trigger = TriggerBuilder.newTrigger().withIdentity("printTrigger", "printtriggergroup")
            .startNow().withSchedule(simpleSchedule().withIntervalInMilliseconds(100l).repeatForever()).build();
      scheduler.scheduleJob(job, trigger);
   }

   public void stopScheduler() throws SchedulerException {
      scheduler.shutdown();
   }

   public static void main(String[] args) {
      PrintScheduler printScheduler = new PrintScheduler(args[0]);
      try {
//       printScheduler.schedule();
         Thread.sleep(60000l);
         printScheduler.stopScheduler();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

}

PrintStatefulJob.java –

package ramstore;

import org.quartz.*;

@PersistJobDataAfterExecution
public class PrintStatefulJob implements Job{

   @Override
   public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
      JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
      Integer count = jobDataMap.getInt("count");
      count++;
      System.out.println("Printing count : "+count);
      jobDataMap.put("count",count);
   }

}

On successful fire of job, it can be observed that the job details and data blob details are updated in the tables of the database.
The job thus stored in the database can be rescheduled in case there is any need. This post deals with rescheduling of jobs from the JDBCJobStore

Code @ Github – https://github.com/vageeshhoskere/blog/tree/master/quartz