Home » Enterprise Java » Quartz » Quartz Scheduler Tutorial

About Ram Mokkapaty

Ram Mokkapaty
Ram holds a master's degree in Machine Design from IT B.H.U. His expertise lies in test driven development and re-factoring. He is passionate about open source technologies and actively blogs on various java and open-source technologies like spring. He works as a principal Engineer in the logistics domain.

Quartz Scheduler Tutorial

In this article, we will look into an example of Quartz Scheduler. We will go through the setup, scheduler factory, scheduler, job, job details, jobDataMap, triggers, and listeners.

If your application has tasks that need to occur at given moments in time, or if your system has recurring maintenance jobs then Quartz may be your ideal solution.

Before we start with the example, let’s look into the basics.

1. What is Quartz?

Quartz is a Java open source job-scheduling system capable of scheduling and executing jobs. In this example, we use the latest stable Quartz available which is version 2.2.1.

Ok, we know what is Quartz but what is job here? A job is a Java class containing the task to be executed but how do we schedule it?. We schedule the job using the trigger which defines when to execute the job. We now know the basic components that Quartz is made up of. Once we start with an example you will know their roles. Let’s start with the Quartz setup.

2. Quartz Setup

In this example, we will use Maven as the build tool so all you need to do is add the below dependency to pom.xml which looks like below.

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javacodegeeks.camel</groupId>
	<artifactId>camelHelloWorld</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
		</dependency>
	</dependencies>
</project>

3. Quartz Scheduler Example

Our example consists of create a schedule, a job that needs to be executed, a trigger that defines when to execute and then finally scheduling the job.

  1. SchedulerFactory – Before you can use the Scheduler, it needs to be instantiated. To do this, we use a SchedulerFactory.
    SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
    
  2. Scheduler – Use the above SchedulerFactory instance to instantiate Scheduler.
    Scheduler scheduler = schedFact.getScheduler();
  3. Star the Scheduler – Once the scheduler is instantiated, it needs to be started. Note triggers will not fire (and therefore, jobs will not execute) until the scheduler has been started. Nor will they fire while the scheduler is in the paused state.
    scheduler.start();
  4. Job – Next we create a job implementing Quartz interface org.quartz.Job. The actual execution code goes into method execute.
  5. JobDataMap provides a mechanism for ‘instance member data’
  6. JobDetail – Conveys the detail properties of a given Job instance.
  7. Trigger – A component that defines the schedule upon which a given Job will be executed.
  8. JobBuilder – This is used to define/build JobDetail instances, which define instances of Jobs. We pass MyJob.class to the builder. We also pass the required job data as an object of JobDataMap.
             JobBuilder jobBuilder = JobBuilder.newJob(MyJob.class);
    	 jobDataMap data = new JobDataMap();
    	 data.put("latch", this);
    		
    	 JobDetail jobDetail = jobBuilder.usingJobData("example", "com.javacodegeeks.quartz.QuartzSchedulerExample")	
    				.usingJobData(data)
    				.withIdentity("myJob", "group1")
    				.build();
    
  9. TriggerBuilder – used to define/build Trigger instances. Here we create a simple schedule that repeats for certain count, each one repeating after certain interval of time.
             Trigger trigger = TriggerBuilder.newTrigger()
    		.withIdentity("myTrigger", "group1")
    		.startNow()
    		.withSchedule(SimpleScheduleBuilder.simpleSchedule()
                    .withRepeatCount(repeatCount)
                    .withIntervalInSeconds(2))		
    		.build();
    

QuartzSchedulerExample:

package com.javacodegeeks.quartz;

import java.util.concurrent.CountDownLatch;

import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;


public class QuartzSchedulerExample implements ILatch {
	private int repeatCount = 3;
	private CountDownLatch latch = new CountDownLatch(repeatCount + 1);
	public static void main(String[] args) throws Exception {	
		QuartzSchedulerExample quartzSchedulerExample = new QuartzSchedulerExample();
		quartzSchedulerExample.fireJob();
	}
	
	public void fireJob() throws SchedulerException, InterruptedException {
		SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
		Scheduler scheduler = schedFact.getScheduler();
		scheduler.start();
		
		// define the job and tie it to our HelloJob class
		JobBuilder jobBuilder = JobBuilder.newJob(MyJob.class);
		JobDataMap data = new JobDataMap();
		data.put("latch", this);
		
		JobDetail jobDetail = jobBuilder.usingJobData("example", "com.javacodegeeks.quartz.QuartzSchedulerExample")	
				.usingJobData(data)
				.withIdentity("myJob", "group1")
				.build();
		
		
		// Trigger the job to run now, and then every 40 seconds
		Trigger trigger = TriggerBuilder.newTrigger()
		.withIdentity("myTrigger", "group1")
		.startNow()
		.withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withRepeatCount(repeatCount)
                .withIntervalInSeconds(2))		
		.build();
		
		// Tell quartz to schedule the job using our trigger
		scheduler.scheduleJob(jobDetail, trigger);
		latch.await();
		System.out.println("All triggers executed. Shutdown scheduler");
		scheduler.shutdown();
	}
	
	public void countDown() {
		latch.countDown();
	}
}

ILatch:

package com.javacodegeeks.quartz;

public interface ILatch {
	void countDown();
}

In our job, we simply print some basic stuff as well we the job data received.

MyJob:

package com.javacodegeeks.quartz;

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

public class MyJob implements Job {
	private static int count;

	public void execute(JobExecutionContext jobContext) throws JobExecutionException {
		System.out.println("--------------------------------------------------------------------");
		System.out.println("MyJob start: " + jobContext.getFireTime());
		JobDetail jobDetail = jobContext.getJobDetail();
		System.out.println("Example name is: " + jobDetail.getJobDataMap().getString("example"));		
		System.out.println("MyJob end: " + jobContext.getJobRunTime() + ", key: " + jobDetail.getKey());
		System.out.println("MyJob next scheduled time: " + jobContext.getNextFireTime());
		System.out.println("--------------------------------------------------------------------");
		
		ILatch latch = (ILatch) jobDetail.getJobDataMap().get("latch");
		latch.countDown();
		count++;
		System.out.println("Job count " + count);
		if (count == 2) {
			throw new RuntimeException("Some RuntimeException!");
		}
		if (count == 4) {
			throw new JobExecutionException("Some JobExecutionException!");
		}
	}

}

You can see here, the job repeats itself for three times after executing the first time. There is an interval of 2 seconds between each execution.

4. JobExecutionException

The only type of exception that Quartz recommends to use in execute() method is the RuntimeException and JobExecutionException. If there are chances of any other exception then you should make sure the code in execute() is wrapped with a ‘trycatch’ block.

You can further configure JobExecutionException using methods like setRefireImmediately() and setUnscheduleFiringTrigger().

MyJob:

package com.javacodegeeks.quartz;

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

public class MyJob implements Job {
	private static int count;

	public void execute(JobExecutionContext jobContext) throws JobExecutionException {
		...
		if (count == 2) {
			throw new RuntimeException("Some RuntimeException!");
		}
		if (count == 4) {
			throw new JobExecutionException("Some JobExecutionException!");
		}
	}

}

5. Run the Example

As you can see the job fires for four times. After the first time, it repeats three more times and then shuts down. Between each job there is an interval of 2 seconds.

Once all the jobs are run, we shutdown the scheduler.

Output:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
--------------------------------------------------------------------
MyJob start: Tue Sep 08 22:19:13 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: Tue Sep 08 22:19:15 IST 2015
--------------------------------------------------------------------
--------------------------------------------------------------------
MyJob start: Tue Sep 08 22:19:15 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: Tue Sep 08 22:19:17 IST 2015
--------------------------------------------------------------------
--------------------------------------------------------------------
MyJob start: Tue Sep 08 22:19:17 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: Tue Sep 08 22:19:19 IST 2015
--------------------------------------------------------------------
--------------------------------------------------------------------
MyJob start: Tue Sep 08 22:19:19 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: null
--------------------------------------------------------------------
All triggers executed. Shutdown scheduler

6. Triggers

There are different types of triggers that you can select based on your scheduling needs. The two most common types are simple triggers and cron triggers. Next two sections, we will show examples of simple as well as cron triggers.

7. Simple Triggers

We already have seen example of simple trigger. We will use it if the job needs to execute exactly once at a specific moment in time, or at a specific moment in time followed by repeats at a specific interval then simple trigger should be OK. For example,

Trigger trigger = TriggerBuilder.newTrigger()
		.withIdentity("myTrigger", "group1")
		.startNow()
		.withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withRepeatCount(repeatCount)
                .withIntervalInSeconds(2))		
		.build();

You can also be specific with time using DateBuilder. Like the below simple trigger is set to start today at a specific hr/min/sec.

Trigger trigger = TriggerBuilder.newTrigger()
		.withIdentity("myTrigger", "group1")
		.startAt(DateBuilder.todayAt(10, 20, 20))
		.withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withRepeatCount(repeatCount)
                .withIntervalInSeconds(2))		
		.build();

Suppose you want the trigger to the job to execute after every 2 second interval and want this to be repeated forever.

Trigger trigger = TriggerBuilder.newTrigger()
		.withIdentity("myTrigger", "group1")
		.startAt(DateBuilder.todayAt(10, 20, 20))
		.withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(2)
                .repeatForever())		
		.build();

8. Cron Triggers

CronTrigger instances are built using TriggerBuilder and another helper class called CronScheduleBuilder which we can use to set the CronTrigger-specific properties. Cron-Expressions are used to configure instances of CronTrigger. Cron-Expressions are strings that are actually made up of seven sub-expressions, that describe individual details of the schedule. These sub-expression are separated with white-space, and represent:

  1. Seconds
  2. Minutes
  3. Hours
  4. Day-of-Month
  5. Month
  6. Day-of-Week
  7. Year (optional field)

Example would be 0 15 10 * * ? * – Fire at 10:15am every day. You can also use some special characters, listed few important ones below:

  1. * every minute if * is placed in minute field
  2. ? useful when you need to specify something in one of the two fields in which the character is allowed, but not the other.
  3. – used to specify ranges.
  4. , to specify additional values
  5. / used to specify increments. For example, “0/15” in the seconds field means “the seconds 0, 15, 30, and 45″.

In the below Example of Crone trigger, the trigger is setup to fire after a min from the current date time.

QuartzSchedulerCronTriggerExample:

package com.javacodegeeks.quartz;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

import org.quartz.CronScheduleBuilder;
import org.quartz.DateBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;


public class QuartzSchedulerCronTriggerExample implements ILatch {
	private CountDownLatch latch = new CountDownLatch(1);
	public static void main(String[] args) throws Exception {	
		QuartzSchedulerCronTriggerExample quartzSchedulerExample = new QuartzSchedulerCronTriggerExample();
		quartzSchedulerExample.fireJob();
	}
	
	public void fireJob() throws SchedulerException, InterruptedException {
		SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
		Scheduler scheduler = schedFact.getScheduler();
		scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener(scheduler));
		scheduler.start();
		
		// define the job and tie it to our HelloJob class
		JobBuilder jobBuilder = JobBuilder.newJob(MyJob.class);
		JobDataMap data = new JobDataMap();
		data.put("latch", this);
		
		JobDetail jobDetail = jobBuilder.usingJobData("example", "com.javacodegeeks.quartz.QuartzSchedulerListenerExample")	
				.usingJobData(data)
				.withIdentity("myJob", "group1")
				.build();
		
		Calendar rightNow = Calendar.getInstance();
		int hour = rightNow.get(Calendar.HOUR_OF_DAY);
		int min = rightNow.get(Calendar.MINUTE);
		
		System.out.println("Current time: " + new Date());
		
		// Fire at curent time + 1 min every day
		Trigger trigger = TriggerBuilder.newTrigger()
		.withIdentity("myTrigger", "group1")
		.startAt(DateBuilder.todayAt(10, 20, 20))
		.withSchedule(CronScheduleBuilder.cronSchedule("0 " + (min + 1) + " " + hour + " * * ? *"))		
		.build();
		
		// Tell quartz to schedule the job using our trigger
		scheduler.scheduleJob(jobDetail, trigger);
		latch.await();
		System.out.println("All triggers executed. Shutdown scheduler");
		scheduler.shutdown();
	}
	
	public void countDown() {
		latch.countDown();
	}
}

Output:

Current time: Wed Sep 09 18:22:38 IST 2015
Job added: myJob
Job scheduled: myTrigger
--------------------------------------------------------------------
MyJob start: Wed Sep 09 18:23:00 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerListenerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: Thu Sep 10 18:23:00 IST 2015
--------------------------------------------------------------------
Job count 1

9. Scheduler Listener

Its time to add a scheduler listener.

A SchedulerListener implements org.quar.SchedulerListener interface. It receives notification of events within the Scheduler itself, for example:

  1. The addition of a job or trigger
  2. The removal of a job or trigger
  3. An error within the Scheduler
  4. The shutdown of the Scheduler

SchedulerListeners are registered with the scheduler’s ListenerManager.

scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener(scheduler));

Let’s first create our own SchedulerListener.

MySchedulerListener:

package com.javacodegeeks.quartz;

import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerListener;
import org.quartz.Trigger;
import org.quartz.TriggerKey;

public class MySchedulerListener implements SchedulerListener {

	private final Scheduler scheduler;

	public MySchedulerListener(Scheduler scheduler) {
		this.scheduler = scheduler;
	}

	public void jobScheduled(Trigger trigger) {
		System.out.println("Job scheduled: " + trigger.getKey().getName());
	}

	public void jobUnscheduled(TriggerKey triggerKey) {
		System.out.println("Job Unscheduled: " + triggerKey.getName());
	}

	public void triggerFinalized(Trigger trigger) {
		System.out.println("Job trigger finalized: "
				+ trigger.getKey().getName());
	}

	public void triggerPaused(TriggerKey triggerKey) {
		System.out.println("Job trigger paused: " + triggerKey.getName());
	}

	public void triggersPaused(String triggerGroup) {
		System.out.println("Job triggers paused for trigger group: "
				+ triggerGroup);
	}

	public void triggerResumed(TriggerKey triggerKey) {
		System.out.println("Job triggers resumed for trigger: " + triggerKey);
	}

	public void triggersResumed(String triggerGroup) {
		System.out.println("Job triggers resumed for trigger group: "
				+ triggerGroup);
	}

	public void jobAdded(JobDetail jobDetail) {
		System.out.println("Job added: " + jobDetail.getKey().getName());
	}

	public void jobDeleted(JobKey jobKey) {
		System.out.println("Job deleted: " + jobKey.getName());
	}

	public void jobPaused(JobKey jobKey) {
		System.out.println("Jobs paused for job: " + jobKey);
	}

	public void jobsPaused(String jobGroup) {
		System.out.println("Jobs paused for job group: " + jobGroup);
	}

	public void jobResumed(JobKey jobKey) {
		System.out.println("Job resumed: " + jobKey.getName());
	}

	public void jobsResumed(String jobGroup) {
		System.out.println("Jobs resumed for job group: " + jobGroup);
	}

	public void schedulerError(String msg, SchedulerException cause) {
		System.out.println("Scheduler Error: " + cause);
	}

	public void schedulerInStandbyMode() {
		try {
			System.out.println("Scheduler put in standby mode: "
					+ scheduler.getSchedulerName());
		} catch (SchedulerException e) {
			System.out.println("Error getting scheduler name" + e);
		}
	}

	public void schedulerStarted() {
		try {
			System.out.println("Scheduler started: "
					+ scheduler.getSchedulerName());
		} catch (SchedulerException e) {
			System.out.println("Error getting scheduler name" + e);
		}
	}

	public void schedulerShutdown() {
		try {
			System.out.println("Scheduler shutdown: "
					+ scheduler.getSchedulerName());
		} catch (SchedulerException e) {
			System.out.println("Error getting scheduler name" + e);
		}
	}

	public void schedulerShuttingdown() {
		try {
			System.out.println("Scheduler shutting down: "
					+ scheduler.getSchedulerName());
		} catch (SchedulerException e) {
			System.out.println("Error getting scheduler name" + e);
		}
	}

	public void schedulingDataCleared() {
		try {
			System.out.println("Scheduler data cleared: "
					+ scheduler.getSchedulerName());
		} catch (SchedulerException e) {
			System.out.println("Error getting scheduler name" + e);
		}
	}

	public void schedulerStarting() {
		try {
			System.out.println("Scheduler starting: "
					+ scheduler.getSchedulerName());
		} catch (SchedulerException e) {
			System.out.println("Error getting scheduler name" + e);
		}
	}

}

We will register the scheduler listener and then schedule the job.

QuartzSchedulerListenerExample:

package com.javacodegeeks.quartz;

import java.util.concurrent.CountDownLatch;

import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;


public class QuartzSchedulerListenerExample implements ILatch {
	private int repeatCount = 3;
	private CountDownLatch latch = new CountDownLatch(repeatCount + 1);
	public static void main(String[] args) throws Exception {	
		QuartzSchedulerListenerExample quartzSchedulerExample = new QuartzSchedulerListenerExample();
		quartzSchedulerExample.fireJob();
	}
	
	public void fireJob() throws SchedulerException, InterruptedException {
		SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
		Scheduler scheduler = schedFact.getScheduler();
		scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener(scheduler));
		scheduler.start();
		
		// define the job and tie it to our HelloJob class
		JobBuilder jobBuilder = JobBuilder.newJob(MyJob.class);
		JobDataMap data = new JobDataMap();
		data.put("latch", this);
		
		JobDetail jobDetail = jobBuilder.usingJobData("example", "com.javacodegeeks.quartz.QuartzSchedulerListenerExample")	
				.usingJobData(data)
				.withIdentity("myJob", "group1")
				.build();
		
		
		// Trigger the job to run now, and then every 40 seconds
		Trigger trigger = TriggerBuilder.newTrigger()
		.withIdentity("myTrigger", "group1")
		.startNow()
		.withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withRepeatCount(repeatCount)
                .withIntervalInSeconds(2))		
		.build();
		
		// Tell quartz to schedule the job using our trigger
		scheduler.scheduleJob(jobDetail, trigger);
		latch.await();
		System.out.println("All triggers executed. Shutdown scheduler");
		scheduler.shutdown();
	}
	
	public void countDown() {
		latch.countDown();
	}
}

As you can see below, we info like ‘Scheduler Starting…’ etc which are from scheduler listener.

Output:

Scheduler starting: DefaultQuartzScheduler
Scheduler started: DefaultQuartzScheduler
Job added: myJob
Job scheduled: myTrigger
--------------------------------------------------------------------
MyJob start: Wed Sep 09 15:10:05 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerListenerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: Wed Sep 09 15:10:07 IST 2015
--------------------------------------------------------------------
Job count 1
--------------------------------------------------------------------
MyJob start: Wed Sep 09 15:10:07 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerListenerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: Wed Sep 09 15:10:09 IST 2015
--------------------------------------------------------------------
Job count 2
Scheduler Error: org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.RuntimeException: Some RuntimeException!]
--------------------------------------------------------------------
MyJob start: Wed Sep 09 15:10:09 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerListenerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: Wed Sep 09 15:10:11 IST 2015
--------------------------------------------------------------------
Job count 3
--------------------------------------------------------------------
MyJob start: Wed Sep 09 15:10:11 IST 2015
Example name is: com.javacodegeeks.quartz.QuartzSchedulerListenerExample
MyJob end: -1, key: group1.myJob
MyJob next scheduled time: null
--------------------------------------------------------------------
Job count 4
All triggers executed. Shutdown scheduler
Job trigger finalized: myTrigger
Scheduler put in standby mode: DefaultQuartzScheduler
Scheduler shutting down: DefaultQuartzScheduler
Job deleted: myJob
Scheduler shutdown: DefaultQuartzScheduler

10. Download the Eclipse Project

This was a tutorial about Quartz Scheduler.

Download
You can download the full source code of this example here: quartzSchedulerExample.zip
(No Ratings Yet)
Start the discussion Views Tweet it!

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

Receive Java & Developer job alerts in your Area

 

Leave a Reply

avatar
  Subscribe  
Notify of