Code for Quartz Scheduler

Overview
Comments
  • Security: XXE in initDocumentParser

    Security: XXE in initDocumentParser

    The method initDocumentParser() in the XMLSchedulingDataProcessor.java does not forbid DTDs, which allows a context-dependend attacker to perfom an XXE. The vulnerability is confirmed working in version 2.2.3.

    I reported this issue to the MITRE cooperation already which assigned the identifier CVE-2019-13990 to this vulnerability. Please confirm the existence of this vulnerability so that the CVE-entry can be completed and published.

    The OWASP Foundation provides an XXE Prevention CheatSheet that explains in detail the steps to prevent this security issue.

    Credits to Andreas Schoedl for working out the relevant code section affected by this issue.

    opened by krabbenpuler 35
  • Quartz Scheduler missing trigger simple_triggers table due to presence of trigger in triggers table

    Quartz Scheduler missing trigger simple_triggers table due to presence of trigger in triggers table

    QuartzSchedulerThread will do as below: for (TriggerKey triggerKey: misfiredTriggers) { OperableTrigger trig = retrieveTrigger(conn, triggerKey); if (trig == null) { continue; }

    retrieveTrigger should return null instead of Exception this caused other triggers to be blocked

    stale 
    opened by srampv 31
  • The jobs recovering (on scheduler startup) blocks simple trigger after failover situation for job which was executing during JVM crash

    The jobs recovering (on scheduler startup) blocks simple trigger after failover situation for job which was executing during JVM crash

    http://www.quartz-scheduler.org/documentation/faq.html

    What is Quartz? ... Quartz is fault-tolerant ...

    But there is a problem with it in some cases.

    Reproduced with Quartz 2.2.1 and 2.2.3 (didn't check other versions)

    Prerequisites

    • Job has simple trigger to repeat execution with some interval (e.g. minute).
    • Concurrent execution is not allowed.
    • Recovery is not requested.

    JVM crashes during a job execution (or was stopped for maintenance during a job execution)

    Downtime is much bigger than trigger's interval (e.g. > 2 minutes)

    Important Quartz tables are TRIGGERS and FIRED_TRIGGERS and theirs states directly after JVM crashed are:

    TRIGGERS table

    | TRIGGER_NAME | TRIGGER_GROUP | JOB_NAME | JOB_GROUP | NEXT_FIRE_TIME | PREV_FIRE_TIME | TRIGGER_STATE | TRIGGER_TYPE | START_TIME | MISFIRE_INSTR | SCHED_NAME | |-------------:|--------------:|---------:|----------:|---------------:|---------------:|--------------:|-------------:|--------------:|--------------:|-----------:| | test | TestJob | test | TestJob | 1481618100000 | 1481618040000 | BLOCKED | SIMPLE | 1481555640000 | 0 | scheduler |

    FIRED_TRIGGERS table

    | ENTRY_ID | TRIGGER_NAME | TRIGGER_GROUP | INSTANCE_NAME | FIRED_TIME | STATE | JOB_NAME | JOB_GROUP | REQUESTS_RECOVERY | SCHED_TIME | IS_NONCONCURRENT | SCHED_NAME | |---------------------------:|-------------:|--------------:|--------------:|--------------:|----------:|---------:|----------:|------------------:|--------------:|-----------------:|-----------:| | NON_CLUSTERED1481617513489 | test | TestJob | NON_CLUSTERED | 1481618049269 | EXECUTING | test | TestJob | 0 | 1481618040000 | 1 | scheduler |

    The 1st scheduler start after system crashed (or stopped for maintenance)

    Below are SQL statements for recoverJobs procedure on scheduler.start() and states of TRIGGERS and FIRED_TRIGGERS tables

    UPDATE TRIGGERS SET TRIGGER_STATE = 'WAITING'
      WHERE SCHED_NAME = 'scheduler' AND (TRIGGER_STATE = 'ACQUIRED' OR TRIGGER_STATE = 'BLOCKED')
    -> trigger has updated from BLOCKED to WAITING
    
    UPDATE TRIGGERS SET TRIGGER_STATE = 'PAUSED' WHERE SCHED_NAME = 'scheduler' AND (TRIGGER_STATE = 'PAUSED_BLOCKED' OR TRIGGER_STATE = 'PAUSED_BLOCKED')
    // not important
    
    SELECT TRIGGER_NAME, TRIGGER_GROUP FROM TRIGGERS
      WHERE SCHED_NAME = 'scheduler' AND NOT (MISFIRE_INSTR = -1) AND NEXT_FIRE_TIME < 1481618263782 AND TRIGGER_STATE = 'WAITING'
      ORDER BY NEXT_FIRE_TIME ASC, PRIORITY DESC
    -> trigger has been selected as misfired in WAITING state
    
    SELECT * FROM TRIGGERS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    SELECT * FROM SIMPLE_TRIGGERS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    SELECT TRIGGER_NAME FROM TRIGGERS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    SELECT TRIGGER_GROUP FROM PAUSED_TRIGGER_GRPS  WHERE SCHED_NAME = 'scheduler' AND TRIGGER_GROUP = 'TestJob'
    SELECT TRIGGER_GROUP FROM PAUSED_TRIGGER_GRPS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_GROUP = '_$_ALL_GROUPS_PAUSED_$_'
    SELECT * FROM JOB_DETAILS WHERE SCHED_NAME = 'scheduler' AND JOB_NAME = 'test' AND JOB_GROUP = 'TestJob'
    // not very important, assume it collects information about misfired trigger and there job
    
    SELECT * FROM FIRED_TRIGGERS
      WHERE SCHED_NAME = 'scheduler' AND JOB_NAME = 'test' AND JOB_GROUP = 'TestJob'
    -> fired trigger has been selected for misfired trigger
    
    UPDATE TRIGGERS SET JOB_NAME = 'test', JOB_GROUP = 'TestJob', DESCRIPTION = NULL, NEXT_FIRE_TIME = 1481618340000, PREV_FIRE_TIME = 1481618040000,
      TRIGGER_STATE = 'BLOCKED', -- !!!! IMPORTANT !!!!
      TRIGGER_TYPE = 'SIMPLE', START_TIME = 1481555640000, END_TIME = 0, CALENDAR_NAME = NULL, MISFIRE_INSTR = 0, PRIORITY = 5, JOB_DATA = '<byte[]>'
      WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    -> (IMPORTANT) trigger which is misfired and fired (because in execution on JVM crash/stop) at the same time
        has been updated to BLOCKED state on scheduler start
    
    UPDATE SIMPLE_TRIGGERS SET REPEAT_COUNT = -1, REPEAT_INTERVAL = 60000, TIMES_TRIGGERED = 1045  WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    // not important
    
    SELECT * FROM FIRED_TRIGGERS WHERE SCHED_NAME = 'scheduler' AND INSTANCE_NAME = 'NON_CLUSTERED' AND REQUESTS_RECOVERY = 1
    // not important, assume handling triggers requested recovery
    
    SELECT TRIGGER_NAME, TRIGGER_GROUP FROM TRIGGERS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_STATE = 'COMPLETE'
    // not important, assume select to remove stale triggers
    
    DELETE FROM FIRED_TRIGGERS WHERE SCHED_NAME = 'scheduler'
    -> fired triggers are removed
    

    TRIGGERS table

    | TRIGGER_NAME | TRIGGER_GROUP | JOB_NAME | JOB_GROUP | NEXT_FIRE_TIME | PREV_FIRE_TIME | TRIGGER_STATE | TRIGGER_TYPE | START_TIME | MISFIRE_INSTR | SCHED_NAME | |-------------:|--------------:|---------:|----------:|---------------:|---------------:|--------------:|-------------:|--------------:|--------------:|-----------:| | test | TestJob | test | TestJob | 1481618100000 | 1481618040000 | BLOCKED | SIMPLE | 1481555640000 | 0 | scheduler |

    FIRED_TRIGGERS table

    No rows

    Problem

    Job has repeat trigger but in BLOCKED state, trigger will not fired, job will not executed at least until JVM is not restarted again

    The 2nd scheduler start (just for test purposes)

    Below are SQL statements for recoverJobs procedure on scheduler.start() and states of TRIGGERS and FIRED_TRIGGERS tables

    UPDATE TRIGGERS SET TRIGGER_STATE = 'WAITING'
      WHERE SCHED_NAME = 'scheduler' AND (TRIGGER_STATE = 'ACQUIRED' OR TRIGGER_STATE = 'BLOCKED')
    -> TRIGGER has updated from BLOCKED to WAITING
    
    UPDATE TRIGGERS SET TRIGGER_STATE = 'PAUSED' WHERE SCHED_NAME = 'scheduler' AND (TRIGGER_STATE = 'PAUSED_BLOCKED' OR TRIGGER_STATE = 'PAUSED_BLOCKED')
    // not important
    
    SELECT TRIGGER_NAME, TRIGGER_GROUP FROM TRIGGERS
      WHERE SCHED_NAME = 'scheduler' AND NOT (MISFIRE_INSTR = -1) AND NEXT_FIRE_TIME < 1481621819212 AND TRIGGER_STATE = 'WAITING'
      ORDER BY NEXT_FIRE_TIME ASC, PRIORITY DESC
    -> trigger has been selected as misfired in WAITING state
    
    SELECT * FROM TRIGGERS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    SELECT * FROM SIMPLE_TRIGGERS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    SELECT TRIGGER_NAME FROM TRIGGERS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    SELECT TRIGGER_GROUP FROM PAUSED_TRIGGER_GRPS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_GROUP = 'TestJob'
    SELECT TRIGGER_GROUP FROM PAUSED_TRIGGER_GRPS WHERE SCHED_NAME = 'scheduler' AND TRIGGER_GROUP = '_$_ALL_GROUPS_PAUSED_$_'
    SELECT * FROM JOB_DETAILS WHERE SCHED_NAME = 'scheduler' AND JOB_NAME = 'test' AND JOB_GROUP = 'TestJob'
    // not very important, assume it collects information about misfired trigger and there job
    
    SELECT * FROM FIRED_TRIGGERS
      WHERE SCHED_NAME = 'scheduler' AND JOB_NAME = 'test' AND JOB_GROUP = 'TestJob'
    -> fired trigger has been selected for misfired trigger
    
    UPDATE TRIGGERS SET JOB_NAME = 'test', JOB_GROUP = 'TestJob', DESCRIPTION = NULL, NEXT_FIRE_TIME = 1481621880000, PREV_FIRE_TIME = 1481618040000,
      TRIGGER_STATE = 'WAITING', -- !!! OK without fired trigger !!!
      TRIGGER_TYPE = 'SIMPLE', START_TIME = 1481555640000, END_TIME = 0, CALENDAR_NAME = NULL, MISFIRE_INSTR = 0, PRIORITY = 5, JOB_DATA = '<byte[]>'
      WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    -> now it is OK because there is not fired trigger
    
    UPDATE SIMPLE_TRIGGERS SET REPEAT_COUNT = -1, REPEAT_INTERVAL = 60000, TIMES_TRIGGERED = 1104 WHERE SCHED_NAME = 'scheduler' AND TRIGGER_NAME = 'test' AND TRIGGER_GROUP = 'TestJob'
    SELECT * FROM FIRED_TRIGGERS WHERE SCHED_NAME = 'scheduler' AND INSTANCE_NAME = 'NON_CLUSTERED' AND REQUESTS_RECOVERY = 1
    SELECT TRIGGER_NAME, TRIGGER_GROUP FROM TRIGGERS  WHERE SCHED_NAME = 'scheduler' AND TRIGGER_STATE = 'COMPLETE'
    DELETE FROM FIRED_TRIGGERS WHERE SCHED_NAME = 'scheduler'
    // not important
    

    Result

    Job with repeat trigger is executed according trigger definition

    BUT BELIEVE it is NOT workaround to restart JVM twice to solve problem. There can be another jobs/triggers in such situation in second restart.

    Setting request recovery to true is not workaround either, in our case we definitely do not need recovery request but job must be executed according trigger interval after JVM restart

    Perhaps workaround to use (before it is fixed in Quartz) is:

      on JVM starting up but before scheduler started
      if (!scheduler.getMetaData().isJobStoreClustered()) {
        // delete all rows from FIRED_TRIGGERS
        // which do not request recovery
        DELETE FROM FIRED_TRIGGERS
          WHERE SCHED_NAME = scheduler.name AND REQUESTS_RECOVERY = 0
      }
      scheduler.start()
    

    See pull request #94

    opened by ghost 25
  • RecurrenceTrigger with iCalendar rfc5545 recurrence rules added.

    RecurrenceTrigger with iCalendar rfc5545 recurrence rules added.

    I have a need to use recurrence rules to define quartz jobs. I read https://jira.terracotta.org/jira/browse/QTZ-252 discussion about it and decide to implement over the solution suggested in the issue. I've also included tests for the new trigger. (Commit checked this time)

    opened by elfogre 22
  • In clustered mode,if do not use acquireTriggersWithinLock properties will appear ABA problem.

    In clustered mode,if do not use acquireTriggersWithinLock properties will appear ABA problem.

    In clustered mode,if do not use acquireTriggersWithinLock properties will appear ABA problem.

    here is the quartz source code:

    public List<OperableTrigger> acquireNextTriggers(final long noLaterThan, final int maxCount, final long timeWindow)
            throws JobPersistenceException {
    if(isAcquireTriggersWithinLock() || maxCount > 1) { 
                lockName = LOCK_TRIGGER_ACCESS;
            } else {
                lockName = null;
            }
    ...
    
    protected List<OperableTrigger> acquireNextTrigger(Connection conn, long noLaterThan, int maxCount, long timeWindow)
            throws JobPersistenceException {
     
            ... 
    
            do {
                currentLoopCount ++;
                try {
                    List<TriggerKey> keys = getDelegate().selectTriggerToAcquire(conn, noLaterThan + timeWindow, getMisfireTime(), maxCount);
                    
                    // No trigger is ready to fire yet.
                    if (keys == null || keys.size() == 0)
                        return acquiredTriggers;
    
                    long batchEnd = noLaterThan;
    
                    for(TriggerKey triggerKey: keys) {
                        
                        ...
    
                        // We now have a acquired trigger, let's add to return list.
                        // If our trigger was no longer in the expected state, try a new one.
                        int rowsUpdated = getDelegate().updateTriggerStateFromOtherState(conn, triggerKey, STATE_ACQUIRED, STATE_WAITING);
                        if (rowsUpdated <= 0) {
                            continue; // next trigger
                        }
                        nextTrigger.setFireInstanceId(getFiredTriggerRecordId());
                        getDelegate().insertFiredTrigger(conn, nextTrigger, STATE_ACQUIRED, null);
    
                        if(acquiredTriggers.isEmpty()) {
                            batchEnd = Math.max(nextTrigger.getNextFireTime().getTime(), System.currentTimeMillis()) + timeWindow;
                        }
                        acquiredTriggers.add(nextTrigger);
                    }
    
                    ...
    
                } catch (Exception e) {
                    throw new JobPersistenceException(
                              "Couldn't acquire next trigger: " + e.getMessage(), e);
                }
            } while (true);
            
            // Return the acquired trigger list
            return acquiredTriggers;
        }
    

    In my configuration, i do not set the batchTriggerAcquisitionMaxCount property, so the default value is 1 and maxCount always equals 1. so acquireNextTriggers() method just have the TRIGGERS table row locks. Then the ABA problem will appear, cluster Server_A get trigger_a, cluster Server_B also can get trigger_a in the acquireNextTrigger() method with selectTriggerToAcquire() method, Server_A change trigger_a status WAITING->ACQUIRED->EXECUTING->WAITING, in this situation Server_B execute updateTriggerStateFromOtherState() method will success, finally the trigger_a will be fired twice.

    I think this is important infomation, but clustering depoly document not mention it. Theoretically, set acquireTriggersWithinLock properties is not enough. TRIGGERS table may be need add version_number field to avoid this problem.

    Here is my two server debug log, Server_A:

    2017-02-17 20:58:12.974 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 0 triggers 2017-02-17 20:58:39.996 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 0 triggers 2017-02-17 20:59:07.272 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 0 triggers 2017-02-17 20:59:31.635 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 1 triggers 2017-02-17 21:00:00.002 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:107] Lock 'TRIGGER_ACCESS' is desired by: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.002 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:92] Lock 'TRIGGER_ACCESS' is being obtained: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.003 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:116] Lock 'TRIGGER_ACCESS' given to: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.008 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:141] Lock 'TRIGGER_ACCESS' returned by: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.008 [DEBUG] [org.quartz.core.JobRunShell:201] Calling execute on job DEFAULT.jobDetail 2017-02-17 21:00:00.013 [DEBUG] [org.apache.http.client.protocol.RequestAddCookies:122] CookieSpec selected: best-match 2017-02-17 21:00:00.013 [DEBUG] [org.apache.http.client.protocol.RequestAuthCache:75] Auth cache not set in the context 2017-02-17 21:00:00.013 [DEBUG] [org.apache.http.impl.conn.PoolingHttpClientConnectionManager:215] Connection request: [route: {}->http://ip:80][total kept alive: 0; route allocated: 0 of 2; total allocated: 0 of 20]

    Server_B:

    2017-02-17 20:58:11.974 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 0 triggers 2017-02-17 20:58:38.915 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 0 triggers 2017-02-17 20:59:08.128 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 0 triggers 2017-02-17 20:59:33.916 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 1 triggers 2017-02-17 21:00:00.001 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:107] Lock 'TRIGGER_ACCESS' is desired by: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.001 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:92] Lock 'TRIGGER_ACCESS' is being obtained: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.002 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:116] Lock 'TRIGGER_ACCESS' given to: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.008 [DEBUG] [org.quartz.impl.jdbcjobstore.StdRowLockSemaphore:141] Lock 'TRIGGER_ACCESS' returned by: scheduler_QuartzSchedulerThread 2017-02-17 21:00:00.009 [DEBUG] [org.quartz.core.JobRunShell:201] Calling execute on job DEFAULT.jobDetail 2017-02-17 21:00:00.013 [DEBUG] [org.quartz.core.QuartzSchedulerThread:276] batch acquisition of 0 triggers 2017-02-17 21:00:00.014 [DEBUG] [org.apache.http.client.protocol.RequestAddCookies:122] CookieSpec selected: best-match 2017-02-17 21:00:00.014 [DEBUG] [org.apache.http.client.protocol.RequestAuthCache:75] Auth cache not set in the context 2017-02-17 21:00:00.014 [DEBUG] [org.apache.http.impl.conn.PoolingHttpClientConnectionManager:215] Connection request: [route: {}->http://ip:80][total kept alive: 0; route allocated: 0 of 2; total allocated: 0 of 20]

    My Cron Expression: 00 00 21 17 02 ? 2017

    My property configuration:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    
        <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
            <property name="jobClass" value="com.netease.mail.yanxuan.scheduler.task.JobInvokeService" />
            <property name="durability" value="true"/>
        </bean>
    
        <bean id="scheduler" lazy-init="false" autowire="no"
              class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    
            <property name="jobDetails">
                <list>
                    <ref bean="jobDetail"/>
                </list>
            </property>
    
            <property name="dataSource" ref="dataSource" />
    
            <property name="overwriteExistingJobs" value="true"/>
    
    
            <property name="quartzProperties">
                <props>
                    <prop key="org.quartz.scheduler.instanceName">EventScheduler</prop>
                    <prop key="org.quartz.scheduler.instanceId">AUTO</prop>
    
                    <!-- Configure ThreadPool -->
                    <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
                    <prop key="org.quartz.threadPool.threadCount">50</prop>
                    <prop key="org.quartz.threadPool.threadPriority">5</prop>
                    <prop key="org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread">true</prop>
    
    
                    <!-- Configure JobStore -->
                    <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreCMT</prop>
                    <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
                    <prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
                    <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
                    <prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">10</prop>
                    <prop key="org.quartz.jobStore.isClustered">true</prop>
                    <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
                    <prop key="org.quartz.jobStore.dontSetAutoCommitFalse">true</prop>
                    <prop key="org.quartz.jobStore.txIsolationLevelSerializable">false</prop>
                    <prop key="org.quartz.jobStore.useProperties">false</prop>
    
                </props>
            </property>
            <property name="applicationContextSchedulerContextKey" value="applicationContext" />
        </bean>
    
    </beans>
    
    <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>
    
    opened by wenniuwuren 22
  • Triggers are getting blocked permanently

    Triggers are getting blocked permanently

    Dear Quartz Team,

    We are using Quartz 2.2.1 in clustered-mode with JDBC job store to schedule jobs marked as @DisallowConcurrentExecution.

    We have observed that occasionally triggers are getting stuck in trigger state BLOCKED without ever recovering automatically. Looking into the job store DB tables, the pattern is always the same:

    • The TRIGGER_STATE on <PREFIX>_TRIGGERS is in state BLOCKED

    • There is no corresponding record in <PREFIX>_FIRED_TRIGGERS

    Obviously org.quartz.impl.jdbcjobstore.JobStoreSupport.clusterRecover(Connection, List<SchedulerStateRecord>) will not recover such triggers, so the only way to get out of this inconsistent state is to manually set the TRIGGER_STATE back to WAITING.

    It is not yet clear under which circumstances this error occurs. However, our log files indicate that jobs getting stuck coincides with temporary database problems.

    Below you can find an example of a NullPointerException in org.quartz.impl.jdbcjobstore.JobStoreSupport.triggersFired(List<OperableTrigger>). The exception itself was caused somewhere in the JDBC driver (Sybase jConnect) when trying to invoke rollback() on a JDBC connection. The log entry’s timestamp correlates exactly with the time the trigger got stuck.

    2017 05 01 20:20:02#+00#ERROR#org.quartz.core.QuartzSchedulerThread##anonymous#ItOpScheduler_Clustered_QuartzSchedulerThread#Runtime error occurred in main trigger firing loop.java.lang.NullPointerException: while trying to invoke the method com.sybase.jdbc4.tds.TdsCursor.setRowNum(int) of a null object loaded from field com.sybase.jdbc4.tds.CurInfo3Token._cursor of an object loaded from local variable 'this'
    	at com.sybase.jdbc4.tds.CurInfo3Token.getMetaInformation(CurInfo3Token.java:85)
    	at com.sybase.jdbc4.tds.CurInfoToken.<init>(CurInfoToken.java:130)
    	at com.sybase.jdbc4.tds.CurInfo3Token.<init>(CurInfo3Token.java:45)
    	at com.sybase.jdbc4.tds.Tds.nextResult(Tds.java:3239)
    	at com.sybase.jdbc4.tds.Tds.readCommandResults(Tds.java:4459)
    	at com.sybase.jdbc4.tds.Tds.doCommand(Tds.java:4444)
    	at com.sybase.jdbc4.tds.Tds.endTransaction(Tds.java:2602)
    	at com.sybase.jdbc4.jdbc.SybConnection.rollback(SybConnection.java:1953)
    	at sun.reflect.GeneratedMethodAccessor492.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:606)
    	at com.sap.core.persistence.jdbc.trace.TraceableBase$1.invoke(TraceableBase.java:44)
    	at com.sun.proxy.$Proxy17.rollback(Unknown Source)
    	at com.sap.core.persistence.jdbc.trace.TraceableConnection.rollback(TraceableConnection.java:239)
    	at org.apache.commons.dbcp.DelegatingConnection.rollback(DelegatingConnection.java:368)
    	at org.apache.commons.dbcp.DelegatingConnection.rollback(DelegatingConnection.java:368)
    	at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.rollback(PoolingDataSource.java:323)
    	at sun.reflect.GeneratedMethodAccessor492.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:606)
    	at org.quartz.impl.jdbcjobstore.AttributeRestoringConnectionInvocationHandler.invoke(AttributeRestoringConnectionInvocationHandler.java:73)
    	at com.sun.proxy.$Proxy143.rollback(Unknown Source)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport.rollbackConnection(JobStoreSupport.java:3658)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3817)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport.triggersFired(JobStoreSupport.java:2908)
    	at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:336)
    |
    

    Please let me know if you need additional details.

    Thanks for your support, Sebastian

    opened by shelmling 20
  • Allow specific jobs or group execution per thread count/limit

    Allow specific jobs or group execution per thread count/limit

    Same as https://jira.terracotta.org/jira/browse/QTZ-258. Just porting it to GitHub for tracking.

    Use-case: We use quartz scheduler (2.0.2) in clustered mode and have a below use case . There are about total 30 jobs and we have org.quartz.threadPool.threadCount=25. This limit gets applied to all the jobs. However we have 3-4 jobs that can be sometimes very long running and resource intensive (not always). So we want to put a limit on the number of these jobs running parallel on a node and cluster.

    This would be really helpful.

    stale 
    opened by hirreddy 18
  • Project abandoned?

    Project abandoned?

    Hi, can someone tell me if this project currently abandoned ... or is this continued because my impression based on the commit history etc. it looks like the project is not continued any more?

    opened by khmarbaise 15
  • JobStoreSupport.triggersFired not using txValidator compensation logic

    JobStoreSupport.triggersFired not using txValidator compensation logic

    I've been digging into a .NET version's problem when database encounters a transient error, like a connectivity problem and thus should be retried. If you look at the code:

    https://github.com/quartz-scheduler/quartz/blob/d42fb7770f287afbf91f6629d90e7698761ad7d8/quartz-core/src/main/java/org/quartz/impl/jdbcjobstore/JobStoreSupport.java#L2976-L3020

    All exceptions are caught in main txCallback and thus I don't see a way how the txValidator logic would ever be called. This will mean that this operation is not retried, unlike the acquireNextTriggers which allows exceptions to bubble up:

    https://github.com/quartz-scheduler/quartz/blob/d42fb7770f287afbf91f6629d90e7698761ad7d8/quartz-core/src/main/java/org/quartz/impl/jdbcjobstore/JobStoreSupport.java#L2802-L2826

    So before changing the behavior on .NET side I was hoping to get some feedback from Java side. Should the errors be allowed to bubble up or is the txValidator just redundant?

    stale 
    opened by lahma 14
  • Allow Job Excution to be pin to a cluster node

    Allow Job Excution to be pin to a cluster node

    It would be nice to able to configure certain specific jobs to allows run in a specific host name in cluster mode.

    Currently users would have to write this logic into their job implementation to skip the work if host names match to some known list.

    Other work around is not to use cluster env and place jobs into individual standalone schedulers.

    stale 
    opened by zemian 14
  • Support job services in OSGi framework with JobStoreTX and JobStoreCMT

    Support job services in OSGi framework with JobStoreTX and JobStoreCMT

    Motivation:
        Lack of support job services in OSGi framework when job store set to JobStoreTX/JobStoreCMT
    
    Modifications:
        Create org.quartz.osgi package that contains follow features:
            - Initialize Quartz bundle with Activator
            - Tracking register/unregister of job services
            - Provide ClassLoaderHelper that can load classes of 
              job services from other bundles
    
        Modify org.quartz.simpl.CascadingClassLoadHelper.java:
            - Add BundleClassLoaderHelper to end of loaders list
    
        Modify quartz-core/pom.xml:
            - Add Bundle-Activator tag
            - Modify Private-Package
    
        Add .gitignore file that to ignore IDE files and etc.
    
    Result:
        The Quartz support job schedule when OSGi job services configured with 
        JobStoreTX/JobStoreCMT
    
    opened by SinaTadayon 14
  • Add MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY type in xsd schema for Cron trigger

    Add MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY type in xsd schema for Cron trigger

    Quartz 2.3.2

    When using quartz jobs.xml for defining jobs and cron triggers, option to set misfire-instruction as MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY is not available in the current xsd schema.

    Code snippet from existing job_scheduling_data_2_0.xsd file:

    <xs:simpleType name="cron-trigger-misfire-instructionType">
            <xs:annotation>
                <xs:documentation>Cron Trigger Misfire Instructions</xs:documentation>
            </xs:annotation>
            <xs:restriction base="xs:string">
                <xs:pattern value="MISFIRE_INSTRUCTION_SMART_POLICY"/>
                <xs:pattern value="MISFIRE_INSTRUCTION_DO_NOTHING"/>
                <xs:pattern value="MISFIRE_INSTRUCTION_FIRE_ONCE_NOW"/>
            </xs:restriction>
    </xs:simpleType>
    

    Please add this misfire instruction type in xsd schema.

    opened by souravdhiman 0
  • Update Introduction.adoc

    Update Introduction.adoc

    An ending paragraph tag

    was removed.

    In submitting this contribution, I agree to the current Software AG contributor agreement as referred to here: https://github.com/quartz-scheduler/contributing/blob/main/CONTRIBUTING.md

    This PR...

    Changes

    Checklist

    • [ ] tested locally
    • [x] updated the docs
    • [ ] added appropriate test
    • [ ] signed-off on the above mentioned SoftwareAG contributor agreement via git commit -s on my commits. (If you're not using command-line, you can use a browser extension )

    Fixes #

    opened by utalmighty 0
  • Add support for Mongo JDBC as JobStore

    Add support for Mongo JDBC as JobStore

    Hello,

    Recently Mongo has released a JDBC driver for their Atlas Services.

    This is the reference site for the mongo JDBC driver: www.mongodb.com/docs/atlas/data-federation/query/sql/drivers/jdbc/connect

    This is the maven artifact: https://search.maven.org/artifact/org.mongodb/mongodb-jdbc

    For the latest version, which at the time of writing this issue is version 2.0.1, the driver Class Name is: com.mongodb.jdbc.MongoDriver

    Can you add support for this driver? i.e. create a suitable SQL file here? https://github.com/quartz-scheduler/quartz/tree/master/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore

    Any help provided is appreciated. Thanks in advance, Erik

    opened by erikrz 0
  • Dependency Convergence Failing.

    Dependency Convergence Failing.

    --- maven-enforcer-plugin:3.1.0:enforce (default-cli) @ xxx-ear --- Downloading from central: https://repo.maven.apache.org/maven2/org/quartz-scheduler/internal/quartz-core/2.3.2/quartz-core-2.3.2.pom [WARNING] Rule 0: org.apache.maven.plugins.enforcer.DependencyConvergence failed with message: Could not build dependency tree Could not collect dependencies:

    opened by ChandanGit13 0
  • CalendarIntervalTriggerTest timezones failing over

    CalendarIntervalTriggerTest timezones failing over

    In CalendarIntervalTriggerTest, references to the invalid 'CEST' timezone are failing over silently to GMT, potentially making the results of the test invalid.

    https://github.com/quartz-scheduler/quartz/blob/ff348f62ece41275555b45dba5a4073c910fbeab/quartz-core/src/test/java/org/quartz/CalendarIntervalTriggerTest.java#L373

    https://github.com/quartz-scheduler/quartz/blob/ff348f62ece41275555b45dba5a4073c910fbeab/quartz-core/src/test/java/org/quartz/CalendarIntervalTriggerTest.java#L384

    Are we certain that daylight savings transitions actually working?

    Kind regards, Joshua J. A. Harwood

    opened by joshuajaharwood 0
  • Improve JDBCDelegate instance retrieval time

    Improve JDBCDelegate instance retrieval time

    Signed-off-by: lokeshAlamuri [email protected]

    In submitting this contribution, I agree to the current Software AG contributor agreement as referred to here: https://github.com/quartz-scheduler/contributing/blob/main/CONTRIBUTING.md

    This PR...

    Changes

    Checklist

    • [x] tested locally
    • [ ] updated the docs
    • [ ] added appropriate test
    • [x] signed-off on the above mentioned SoftwareAG contributor agreement via git commit -s on my commits. (If you're not using command-line, you can use a browser extension )

    Fixes #

    opened by LokeshAlamuri 0
Releases(v2.3.2)
  • v2.3.2(Oct 23, 2019)

    This a bug fix release containing fixes for:

    • #508 : Error with H2 1.4.200
    • #505 : CronTrigger.getTriggerBuilder() changes misfire instruction from "ignore misfire" to "smart"
    • #491 : StdJDBCDelegate.selectTriggerToAcquire may not respect maxCount
    • #490 : Return at most maxCount triggers
    • #482 : Update C3P0 version to 0.9.5.4 (CVE-2019-5427)
    • #474 : StdSchedulerFactory ConcurrentModificationException reading system properties
    • #467 : Security: XXE in initDocumentParser
    Source code(tar.gz)
    Source code(zip)
  • quartz-2.3.1(Mar 27, 2019)

    THIS RELEASE REQUIRES JDK7

    • #294 depen: Update hikaricp-java6:2.3.13 to hikaricp-java7:2.4.13
    • #316 depen: Updated C3P0 version to 0.9.5.3
    • #147 bugfix: Fix BINARY to BLOG type for job data for hsqldb
    • #156 bugfix: Fix null string used in thread name with DirectSchedulerFactory
    • #159 bugfix: Fix extra bad char tick on drop table qurtz_fired_triggers for postgres
    • #146 bugfix: Release BLOCKED triggers in releaseAcquiredTrigger
    • #212 bugfix: QuartzInitializerListener: fix a typo
    • #193 bugfix: Job execution context impl returns incorrect recovering job key
    • #172 bugfix: Miss notify SchedulerListeners in QuartzScheduler.java
    • #220 bugifx: DailyTimeIntervalTrigger failed to set endingDailyAfterCount = 1
    • #160 improv: Add drop table if exists check in sql script for postgres
    • #214 improv: Reuse JobBuilder.storeDurably(boolean) in JobBuilder
    • #281 improv: Fix no setter for dataSource property validateOnCheckout
    • #264 improv: Fix no setter for dataSource property discardIdleConnectionsSeconds
    • #245 improv: Sybase: Changed varchar length TRIGGER_NAME from 80 to 200
    • #340 improv: Use all-caps table names in the liquibase script
    • #189 improv: NPE thrown when acquiring next trigger due to null next fire time value
    • #268 improv: Add configurable params for StdRowLockSemaphore for Failure obtaining db row lock
    • #293 build: Setup Azure CI server for Quartz project
    • #66 build: Remove unused 'svn' requirement during maven package build
    • #301 build: Improve project with readme, and license changes log
    • #302 build: Update mvnw wrapper to use Maven 3.6.0
    • #226 build: Replace maven-forge-plugin with maven-jar-plugin
    • #170 docs: Minor fix and improvement on Javadoc
    • #203 docs: Minor fix and improvement on Javadoc
    • #360 docs: Update docs and migrate it into main source repository
    Source code(tar.gz)
    Source code(zip)
Owner
Quartz Job Scheduler
A full-featured, Java-based, In-process job scheduler.
Quartz Job Scheduler
Persistent cluster-friendly scheduler for Java

db-scheduler Task-scheduler for Java that was inspired by the need for a clustered java.util.concurrent.ScheduledExecutorService simpler than Quartz.

Gustav Karlsson 714 Dec 31, 2022
Daily mail subscription implementation using Java Spring-boot and Quartz Scheduler

Daily Mail Subscription Service POC Implemented using Java Spring-boot and Quartz Scheduler Working Application Exposing 3 endpoints /subscription/cre

null 16 Jun 3, 2022
Projecto Spring with Quartz

Spring Boot Quartz Scheduler : Building an Email Scheduling Requirements Java - 8 Maven - 3.x.x > MySQL - 5.x.x > Steps to Setup 1. Clone the applicat

jrojast 1 Jan 25, 2022
A simple Java Scheduler library with a minimal footprint and a straightforward API

Wisp Scheduler Wisp is a library for managing the execution of recurring Java jobs. It works like the Java class ScheduledThreadPoolExecutor, but it c

Coreoz 105 Dec 31, 2022
Persistent cluster-friendly scheduler for Java

db-scheduler Task-scheduler for Java that was inspired by the need for a clustered java.util.concurrent.ScheduledExecutorService simpler than Quartz.

Gustav Karlsson 714 Dec 31, 2022
cglib - Byte Code Generation Library is high level API to generate and transform Java byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.

cglib Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to g

Code Generation Library 4.5k Jan 8, 2023
vʌvr (formerly called Javaslang) is a non-commercial, non-profit object-functional library that runs with Java 8+. It aims to reduce the lines of code and increase code quality.

Vavr is an object-functional language extension to Java 8, which aims to reduce the lines of code and increase code quality. It provides persistent co

vavr 5.1k Jan 3, 2023
Code metrics for Java code by means of static analysis

CK CK calculates class-level and method-level code metrics in Java projects by means of static analysis (i.e. no need for compiled code). Currently, i

Maurício Aniche 286 Jan 4, 2023
mobsfscan is a static analysis tool that can find insecure code patterns in your Android and iOS source code.

mobsfscan is a static analysis tool that can find insecure code patterns in your Android and iOS source code. Supports Java, Kotlin, Swift, and Objective C Code. mobsfscan uses MobSF static analysis rules and is powered by semgrep and libsast pattern matcher.

Mobile Security Framework 347 Dec 29, 2022
Business Application Platform - no-code/low-code platform to build business applications

Orienteer What is Orienteer Orienteer is Business Application Platform: Easy creation of business applications Extendable to fit your needs Dynamic da

Orienteer 189 Dec 6, 2022
Team 5468's 2022 FRC robot code. This code is written in Java and is based off of WPILib's Java control system and utilizes a command based system

FRC 2022 Team 5468's 2022 FRC robot code. This code is written in Java and is based off of WPILib's Java control system and utilizes a command based s

null 4 Oct 4, 2022
This template makes it easy to organize FTC code and allows for the Autonomous and TeleOp periods to share code.

FTC Code Organizer This template created by team 19458 Equilibrium.exe makes it easy to keep your code organized and allows the Autonomous and TeleOp

FTC 19458 Equilibrium.exe 5 Nov 10, 2022
A tool to help eliminate NullPointerExceptions (NPEs) in your Java code with low build-time overhead

NullAway: Fast Annotation-Based Null Checking for Java NullAway is a tool to help eliminate NullPointerExceptions (NPEs) in your Java code. To use Nul

Uber Open Source 3.2k Dec 29, 2022
An extensible multilanguage static code analyzer.

PMD About PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and

PMD 4.1k Jan 2, 2023
:coffee: SonarSource Static Analyzer for Java Code Quality and Security

Code Quality and Security for Java This SonarSource project is a code analyzer for Java projects. Information about the analysis of Java features is a

SonarSource 976 Jan 5, 2023
Inria 1.4k Dec 29, 2022
SpotBugs is FindBugs' successor. A tool for static analysis to look for bugs in Java code.

SpotBugs is the spiritual successor of FindBugs, carrying on from the point where it left off with support of its community. SpotBugs is licensed unde

null 2.9k Jan 4, 2023