Quartz

Quartz ist ein Framework, mit dem man zeitgesteuert Code ausführen lassen kann.

Quartz Logging

Quartz nutzt . Zum Start slf4j-api-?.jar und slf4j-simple-?.jar in den Klassenpfad aufnehmen. Letzteres kann man dann z.B. durch slf4j-log4j12-?.jar ersetzen, wenn man lieber log4j benutzen möchte.

quartz.properties

# This scheduler's name will be "MyScheduler".
org.quartz.scheduler.instanceName = MyScheduler

# Max Number of parallel threads
org.quartz.threadPool.threadCount = 5

# store jobs in RAM instead of a real DB
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

Quartz Beispiele

Quartz Job:

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

public class MySimpleJob implements Job
{      
        @Override
        public void execute(JobExecutionContext jobexecutioncontext) throws JobExecutionException
        {
...
        }


}
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import static org.quartz.JobBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.TriggerBuilder.*;

public class Main
{
...
            // Grab the Scheduler instance from the Factory
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // and start it off
            scheduler.start();

            // the job shall be started 8 times
            JobDetail job = newJob(MySimpleJob.class).withIdentity("job1", "group1").build();
            SimpleScheduleBuilder sb=SimpleScheduleBuilder.repeatSecondlyForTotalCount(8);

            // Trigger the job to run now, and then repeat every 40 seconds
            Trigger trigger = newTrigger()
                        .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(sb)
                    .build();

            // Tell quartz to schedule the job using our trigger
            scheduler.scheduleJob(job, trigger);

             // give the processes time to finish
            Thread.sleep(5*60*1000);

            scheduler.shutdown();
...
}

JobDataMap

Man kann den neu angelegten Jobs noch Werte über eine eingebaute Map mitgeben. Diese Map wird bei Bedarf sogar mit dem Job zusammen persistiert (wenn die Werte serialisierbar sind).

JobDetail job = newJob(MySimpleJob.class)
    .withIdentity("job1", "group1")
    .usingJobData("MY_MESSAGE", "Hello World")
    .build();

Und so können die Werte im Job dann wieder ausgelesen werden

@Override
public void execute(JobExecutionContext jobexecutioncontext) throws JobExecutionException
{
  JobDataMap dataMap=jobexecutioncontext.getJobDetail().getJobDataMap();
  String lMessage=dataMap.getString("MY_MESSAGE");
}

Falls es im Job Objekt Setter gibt, die wie ein Key aus der Map heißen, werden diese auch aufgerufen. z.B.

public void setMY_MESSAGE(String pValue) { ... }

Quartz Annotation

@DisallowConcurrentExecution

Der Job, der so annotiert wurde, darf nicht parallel zu einem anderen Job mit der gleichen Klasse laufen

Quartz Cron

Quartz Cron

/*
 * Quartz Cron syntax
 *
 * Seconds Minutes Hours Day-of-Month Month Day-of-Week Year(optional)
 *
 * Day-of-Weeks:
 * MON
 * TUE
 * WED
 * THU
 * FRI
 * SAT
 * SUN
 * MON-FRI
 * MON,WED,FRI
 * MON-WED,SAT
 * * (every day in the week)
 * ? day of the week is not relevant
 *
 * Day-of-Month
 * 1 first day in month
 * L last day in month
 * * any day in month
 * ? day in month is not relevant
 *
 */
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// and start it off
scheduler.start();

JobDetail job1 = newJob(MySimpleJob.class)
                    .withIdentity("job1", "group1")
                    .build();

// Noon
String cronString_noon="0 0 12 ? * *";

// Noon on Wednesday
String cronString_noon_wednesday="0 0 12 ? * WED";

// Every 6 minutes of the hour (10:06, 10:12, ..., 10:54, 11:06, etc)
String cronString_every_6="0 0/6 * * * ?";

// Every 35 minute of the hour (10:35, 11:35, 12:35, etc)
String cronString_every_35="0 0/35 * * * ?";

Trigger trigger_noon = newTrigger()
        .withIdentity("trigger_noon", "group1")
        .withSchedule(cronSchedule(cronString_noon))
        .build();

Trigger trigger_noon_wednesday = newTrigger()                    
        .withIdentity("trigger_noon_wednesday", "group1")
        .withSchedule(cronSchedule(cronString_noon_wednesday))
        .build();

Trigger trigger_every_6 = newTrigger()                    
        .withIdentity("trigger_every_6", "group1")
        .withSchedule(cronSchedule(cronString_every_6))
        .build();

Trigger trigger_every_35 = newTrigger()                    
        .withIdentity("trigger_every_35", "group1")
        .withSchedule(cronSchedule(cronString_every_35))
        .build();

// Tell Quartz to schedule the job using our trigger
scheduler.scheduleJob(job1, trigger_noon);
scheduler.scheduleJob(job2, trigger_noon_wednesday);
scheduler.scheduleJob(job3, trigger_every_6);
scheduler.scheduleJob(job4, trigger_every_35);

Listeners

Es gibt

  • JobListener
  • SchedulerListeners

Job Stores

RAMJobStore

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

JDBCJobStore

Quartz herunterladen, unter docs/dbTables/ findet man dann für verschiedene DB Scripte um die Tabellen anzulegen, die Quartz für den JDBCJobStore benötigt. Diese Tabellen in der DB anlegen. Man benötigt das c3p0 jar im Klassenpfad und JDBC Treiber für die DB, z.B. ojdbc6.jar für Oracle.

# Persist Quartz status into an Oracle DB, manage Oracle DB transactions
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

# Oracle
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myQuartzDataSource
org.quartz.jobStore.useProperties = false

# Configure Datasources  
org.quartz.dataSource.myQuartzDataSource.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.myQuartzDataSource.URL = jdbc:oracle:thin:@myComputer.example.com:1521:myDB
org.quartz.dataSource.myQuartzDataSource.user = MrFoo
org.quartz.dataSource.myQuartzDataSource.password = SuperSecretPW123
org.quartz.dataSource.myQuartzDataSource.maxConnections = 5
org.quartz.dataSource.myQuartzDataSource.validationQuery=select 0 from dual

misfireThreshold

Wie lange darf ein Job seine Ausführzeit verpassen bis sie als verpasst angesehen wird? Angabe in Milliseconden, Default ist 60.000 (1 Minute)

org.quartz.jobStore.misfireThreshold 60000

Quartz Exceptionhandling

@Override
public void execute(JobExecutionContext jobexecutioncontext) throws JobExecutionException
{
    try
    {
     ...
    }
    catch(NullPointerException e)
    {
      JobExecutionException e_new=new JobExecutionException(e);
      // permanent error, never try to execute this job again
      e_new.setUnscheduleAllTriggers(true);
      throw e_new;
    }
    catch(Exception e)
    {
      JobExecutionException e_new=new JobExecutionException(e);
      // try to execute this job right way
      e_new.refireImmediately();
      throw e_new;
    }    
}