Quartz API'sinde Cron aşağıdaki gibi 7 bölümden oluşmuştır :
(Saniye) (Dakika) (Saat) (Ayın Günü) (Ay) (Haftanın Günü) (Yıl)
Örnek : 0 15 10 * * ? 2005 (2005 yılında her gün 10'u 15 geçe)
* Yıl bölümünü kullanmak zorunlu değildir.
Her bölümde kullanılacak karakterler aşağıdaki gibi olabilir :
Saniye : 0-59 , - * /
Dakika : 0-59 , - * /
Saat : 0-59 , - * /
Ayın Günü : 1-31 , - * / ? L W
Ay : 1-12 veya JAN-DEC , - * /
Haftanın Günü : 1-7 veya SUN-SAT , - * / ? L #
Year : 1970-2099 , - * /
Karakterlerin Anlamı
* :Tüm değerler için kullanılır
? :Herhangi bir değer için kullanılır
, :Birden fazla değer vermek için kullanılır.
- :Aralık vermek için kulanılır. Örneğin saat değeri 10-13 ise saat 10,11,12,13 anlaşılır
/ :Belirli değerde arttırmak için kullanılır. Örneğin saniye değeri için 0/15 ifadesi saniye değeri 0 ,15, 30, 45 anlamına gelmektedir.
L :Last(son) anlamına gelmektedir. L Ayın son günü veya haftanın son günü için kullanılır.
W :Weekday (İş günü) anlamına gelmektedir.
# :".nci günü" anlamına gelmektedir. Örneğin haftanın günü için 6#3 ifadesi ayın 3. cuması anlamına gelmektedir.
Quartz API, Load Balancing ve Cluster desteği bulunmaktadır. Birden fazla server üzerinde tek bir server'da çalışır gibi çalışabilmektedir.
Dağıtık çalışabilmesi için veriler bir ilişkisel veritabanında saklanmalı ve JDBC ile ona erişilmelidir. Konfigürasyonda dağıtık (load balancing veya cluster ortamında) ortamda çalışıldığı bilgisi tanımlanmalıdır.
Veritabanındaki kitleme sistemi sayesinde aynı anda bir Job iki veya daha fazla server'da çalışamaz.
#onestep.jdbc.driverClassName = org.h2.Driver #onestep.jdbc.url = jdbc:h2:file:${quartzdesk-web.work.dir}/h2/quartzdesk #onestep.jdbc.username = quartzdesk #onestep.jdbc.password = quartzdesk #onestep.jdbc.pool.initialSize = 1 #onestep.jdbc.pool.maxTotal = 20 #onestep.jdbc.pool.minIdle = 1 #onestep.jdbc.pool.maxIdle = 1 #onestep.jdbc.pool.maxWaitMillis = 5000 #onestep.jdbc.pool.validationQuery = select 1Eğer bunun yerine datasource kullanmak istiyorsanız Tomcat'de conf içinde server.xml'de GlobalNamingResources elementi içine aşağıdakini ekleyebilirsiniz. Datasource kullanmanızı tavsiye ederiz.
<Resource name="jdbc/QuartzDeskDS" auth="Container" type="javax.sql.DataSource" removeAbandoned="true" removeAbandonedTimeout="30" maxActive="10" maxIdle="1" maxWait="2000" validationQuery="select 1" poolPreparedStatements="true" username="quartzdesc" password="quartzdesc" driverClassName="org.h2.Driver" url="jdbc:h2:file:C:\Users\test\quartzdesc.mv.db"/>Kullandığınız veritabanın driver'ını Tomcat'in lib dizinine ekleyin. Böylece veritabanı sürücüsü classpath'de olacaktır.
<?xml version="1.0"?> <Context> <Parameter name="quartzdesk-web.work.dir" value="E:\Programs\quartzdesc"/> <ResourceLink name="QuartzDeskDS" global="jdbc/QuartzDeskDS" type="javax.sql.DataSource"/> </Context>quartzdesk-web.work.dir değeri Tomcat açılırken parametre olarak
set CATALINA_OPTS=%CATALINA_OPTS% -Dquartzdesk-web.work.dir=Q:\Programs\quartzdescşeklinde de verilebilir. Bu ifade setEnv veya doğrudan catalina.bat'a ekleyebilirsiniz.
<?xml version="1.0" encoding="UTF-8"?> <job-scheduling-data xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_1_8.xsd" version="1.8"> <schedule> <job> <name>JobA</name> <group>GroupDummy</group> <description>This is Job A</description> <job-class>com.thy.mercury.jobs.HelloJob</job-class> </job> <trigger> <cron> <name>dummyTriggerNameA</name> <job-name>JobA</job-name> <job-group>GroupDummy</job-group> <cron-expression>0/5 * * * * ?</cron-expression> </cron> </trigger> </schedule> <schedule> <job> <name>JobB</name> <group>GroupDummy</group> <description>This is Job B</description> <job-class>com.thy.mercury.jobs.HelloJob2</job-class> </job> <trigger> <cron> <name>dummyTriggerNameB</name> <job-name>JobB</job-name> <job-group>GroupDummy</job-group> <cron-expression>0/5 * * * * ?</cron-expression> </cron> </trigger> </schedule>HelloWorld job'ları aşağıdakine benzer olabilir:
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class HelloJob implements Job { @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println("Hello Job"); } }Scheduler sistemini ayağa kaldırmak için uygulama aşağıdaki gibi olabilir:
import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.impl.StdSchedulerFactory; public class JobsApplication { public static void main(String[] args) { try { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } }Job'a aşağıdaki gibi parametre de ekleyebilirsiniz:
<job> <name>JobA</name> <group>GroupDummy</group> <description>This is Job A</description> <job-class>com.thy.mercury.jobs.HelloJob</job-class> <job-data-map> <entry> <key>Name</key> <value>World</value> </entry> </job-data-map> </job>Bu parametreler Job'ın içinde aşağıdaki gibi alınabilir:
System.out.println("Hello Job"); JobDataMap map=jobExecutionContext.getJobDetail().getJobDataMap(); for (String key : map.keySet()) { System.out.println(key+"="+map.getString(key)); }XML'de yapılan değişiklikler 10 sn sonra devreye alınmaktadır. Eğer Job'ları kaldırmak istiyorsanız XML'in başına aşağıdakini ekleyebilirsiniz :
<pre-processing-commands> <delete-job> <name>JobA</name> <group>GroupDummy</group> </delete-job> <delete-trigger> <name>dummyTriggerNameA</name> </delete-trigger> </pre-processing-commands>
Aşağıdaki gibi quartz.properties dosyasını classpath'e eklenmesi gerekir (örneğin src klasörünün root'na eklenebilir)org.quartz-scheduler quartz 2.2.2 org.quartz-scheduler quartz-jobs 2.2.2
org.quartz.scheduler.instanceName = MyScheduler org.quartz.threadPool.threadCount = 3 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStoreorg.quartz.scheduler.instanceName = MyScheduler sistem bir isim vermek için kullanılır. org.quartz.threadPool.threadCount değeri anda çalışacak thread sayısını sınırlamaktadır. org.quartz.jobStore.class ise job'ların RAM'de saklanacağı ve yönetileceği anlamına gelmektedir.
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class HelloWorldJob implements Job{ @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println("Hello World"); } }Bu job'ın çalışması için Scheduler sistemini başlatan, bu job'ı çalışması için bir sınıf aşağıdaki gibi yapılabilir:
import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; import static org.quartz.JobBuilder.*; import static org.quartz.TriggerBuilder.*; import static org.quartz.SimpleScheduleBuilder.*; import org.quartz.JobDetail; public class SchedulerManager { public void init() throws SchedulerException{ SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); JobDetail jobDetail = newJob(HelloWorldJob.class) .withIdentity("job1", "group1") .build(); Trigger trigger = newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(10) .repeatForever()) .build(); scheduler.scheduleJob(jobDetail, trigger); } public static void main(String[] args) throws SchedulerException { SchedulerManager manager=new SchedulerManager(); manager.init(); } }scheduler.start() ile scheduler sistemi başlatılır. JobDetail sınıfı ile HelloWorld job'ı tanımlanır. Bu job'ın hangi aralıklarla çalışacağını belirlemek için bir Trigger yaratılır. Örnekte yaratılan trigger 10 sn'de bir HelloWorldJob'ın çalışmasını sağlayacak şekildedir. scheduler.scheduleJob(jobDetail, trigger) ile bu işlem yapılmıştır.
import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.annotation.WebListener; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.ee.servlet.QuartzInitializerListener; import org.quartz.impl.StdSchedulerFactory; @WebListener public class QuartzListener extends QuartzInitializerListener { @Override public void contextInitialized(ServletContextEvent sce) { try { super.contextInitialized(sce); ServletContext ctx = sce.getServletContext(); StdSchedulerFactory schedulerFactory = (StdSchedulerFactory) ctx .getAttribute(QUARTZ_FACTORY_KEY); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); JobDetail jobDetail = newJob(HelloWorldJob.class) .withIdentity("job1", "group1") .build(); Trigger trigger = newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(cronSchedule("0/10 * * * * ?")) .build(); scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } }
Bir Job çalışmasını tamamlayamadan diğer Job'ın çalışma zamanı gelirse normal durumlarda diğer Job'da çağrılır. Örneğin 10 sn bir çalışması gerekn bir Job'ta bir Job'ın işi 10 sn'de bitmez ise diğer Job'ta çalışmaya başlar. Bu şekilde aynı anda çok sayıda Job çalışabilir. Eğer bu durumun oluşmasını istemiyorsanız Job interface'si yerine StatefulJob interface'sini implement ediniz.