Skip to content

Commit

Permalink
add resetTriggerFromErrorState functionality from quartz-scheduler#25
Browse files Browse the repository at this point in the history
  • Loading branch information
jhouserizer committed Apr 15, 2017
1 parent 2f803c8 commit b886159
Show file tree
Hide file tree
Showing 13 changed files with 255 additions and 15 deletions.
16 changes: 16 additions & 0 deletions quartz-core/src/main/java/org/quartz/Scheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,22 @@ Trigger getTrigger(TriggerKey triggerKey)
TriggerState getTriggerState(TriggerKey triggerKey)
throws SchedulerException;

/**
* Reset the current state of the identified <code>{@link Trigger}</code>
* from {@link TriggerState#ERROR} to {@link TriggerState#NORMAL} or
* {@link TriggerState#PAUSED} as appropriate.
*
* <p>Only affects triggers that are in ERROR state - if identified trigger is not
* in that state then the result is a no-op.</p>
*
* <p>The result will be the trigger returning to the normal, waiting to
* be fired state, unless the trigger's group has been paused, in which
* case it will go into the PAUSED state.</p>
*
* @see Trigger.TriggerState
*/
void resetTriggerFromErrorState(TriggerKey triggerKey)
throws SchedulerException;
/**
* Add (register) the given <code>Calendar</code> to the Scheduler.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,13 @@ public TriggerState getTriggerState(TriggerKey triggerKey) throws SchedulerExcep
return resources.getJobStore().getTriggerState(triggerKey);
}


public void resetTriggerFromErrorState(TriggerKey triggerKey) throws SchedulerException {
validateState();

resources.getJobStore().resetTriggerFromErrorState(triggerKey);
}

/**
* <p>
* Add (register) the given <code>Calendar</code> to the Scheduler.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ public interface RemotableQuartzScheduler extends Remote {

TriggerState getTriggerState(TriggerKey triggerKey) throws SchedulerException, RemoteException;

void resetTriggerFromErrorState(TriggerKey triggerKey) throws SchedulerException, RemoteException;

void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException, RemoteException;

boolean deleteCalendar(String calName) throws SchedulerException, RemoteException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,22 @@ public TriggerState getTriggerState(TriggerKey triggerKey)
new String[] { String.class.getName(), String.class.getName() }));
}


/**
* <p>
* Calls the equivalent method on the 'proxied' <code>QuartzScheduler</code>,
* passing the <code>SchedulingContext</code> associated with this
* instance.
* </p>
*/
public void resetTriggerFromErrorState(TriggerKey triggerKey)
throws SchedulerException {
invoke(
"resetTriggerFromErrorState",
new Object[] { triggerKey.getName(), triggerKey.getGroup() },
new String[] { String.class.getName(), String.class.getName() });
}

/**
* <p>
* Calls the equivalent method on the 'proxied' <code>QuartzScheduler</code>,
Expand Down
18 changes: 18 additions & 0 deletions quartz-core/src/main/java/org/quartz/impl/RemoteScheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,24 @@ public TriggerState getTriggerState(TriggerKey triggerKey)
}
}

/**
* <p>
* Calls the equivalent method on the 'proxied' <code>QuartzScheduler</code>.
* </p>
*/
public void resetTriggerFromErrorState(TriggerKey triggerKey)
throws SchedulerException {
try {
getRemoteScheduler().resetTriggerFromErrorState(triggerKey);
} catch (RemoteException re) {
throw invalidateHandleCreateException(
"Error communicating with remote scheduler.", re);
}
}




/**
* <p>
* Calls the equivalent method on the 'proxied' <code>QuartzScheduler</code>.
Expand Down
19 changes: 19 additions & 0 deletions quartz-core/src/main/java/org/quartz/impl/StdScheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,25 @@ public TriggerState getTriggerState(TriggerKey triggerKey)
return sched.getTriggerState(triggerKey);
}

/**
* Reset the current state of the identified <code>{@link Trigger}</code>
* from {@link TriggerState#ERROR} to {@link TriggerState#NORMAL} or
* {@link TriggerState#PAUSED} as appropriate.
*
* <p>Only affects triggers that are in ERROR state - if identified trigger is not
* in that state then the result is a no-op.</p>
*
* <p>The result will be the trigger returning to the normal, waiting to
* be fired state, unless the trigger's group has been paused, in which
* case it will go into the PAUSED state.</p>
*
* @see Trigger.TriggerState
*/
public void resetTriggerFromErrorState(TriggerKey triggerKey)
throws SchedulerException {
sched.resetTriggerFromErrorState(triggerKey);
}

/**
* <p>
* Calls the equivalent method on the 'proxied' <code>QuartzScheduler</code>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1601,6 +1601,47 @@ public TriggerState getTriggerState(Connection conn, TriggerKey key)
}
}

/**
* Reset the current state of the identified <code>{@link Trigger}</code>
* from {@link TriggerState#ERROR} to {@link TriggerState#NORMAL} or
* {@link TriggerState#PAUSED} as appropriate.
*
* <p>Only affects triggers that are in ERROR state - if identified trigger is not
* in that state then the result is a no-op.</p>
*
* <p>The result will be the trigger returning to the normal, waiting to
* be fired state, unless the trigger's group has been paused, in which
* case it will go into the PAUSED state.</p>
*/
public void resetTriggerFromErrorState(final TriggerKey triggerKey) throws JobPersistenceException {
executeInLock(
LOCK_TRIGGER_ACCESS,
new VoidTransactionCallback() {
public void executeVoid(Connection conn) throws JobPersistenceException {
resetTriggerFromErrorState(conn, triggerKey);
}
});
}

void resetTriggerFromErrorState(Connection conn, final TriggerKey triggerKey)
throws JobPersistenceException {

try {
String newState = STATE_WAITING;

if(getDelegate().isTriggerGroupPaused(conn, triggerKey.getGroup())) {
newState = STATE_PAUSED;
}

getDelegate().updateTriggerStateFromOtherState(conn, triggerKey, newState, STATE_ERROR);

getLog().info("Trigger " + triggerKey + " reset from ERROR state to: " + newState);
} catch (SQLException e) {
throw new JobPersistenceException(
"Couldn't reset from error state of trigger (" + triggerKey + "): " + e.getMessage(), e);
}
}

/**
* <p>
* Store the given <code>{@link org.quartz.Calendar}</code>.
Expand Down Expand Up @@ -3701,7 +3742,7 @@ protected void commitConnection(Connection conn)
* the a transaction template. If no return value is required, execute
* should just return null.
*
* @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback)
* @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback, TransactionValidator)
* @see JobStoreSupport#executeInLock(String, TransactionCallback)
* @see JobStoreSupport#executeWithoutLock(TransactionCallback)
*/
Expand All @@ -3717,7 +3758,7 @@ protected interface TransactionValidator<T> {
* Implement this interface to provide the code to execute within
* the a transaction template that has no return value.
*
* @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback)
* @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback, TransactionValidator)
*/
protected abstract class VoidTransactionCallback implements TransactionCallback<Void> {
public final Void execute(Connection conn) throws JobPersistenceException {
Expand Down
36 changes: 36 additions & 0 deletions quartz-core/src/main/java/org/quartz/simpl/RAMJobStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,42 @@ public TriggerState getTriggerState(TriggerKey triggerKey) throws JobPersistence
}
}

/**
* Reset the current state of the identified <code>{@link Trigger}</code>
* from {@link TriggerState#ERROR} to {@link TriggerState#NORMAL} or
* {@link TriggerState#PAUSED} as appropriate.
*
* <p>Only affects triggers that are in ERROR state - if identified trigger is not
* in that state then the result is a no-op.</p>
*
* <p>The result will be the trigger returning to the normal, waiting to
* be fired state, unless the trigger's group has been paused, in which
* case it will go into the PAUSED state.</p>
*/
public void resetTriggerFromErrorState(final TriggerKey triggerKey) throws JobPersistenceException {

synchronized (lock) {

TriggerWrapper tw = triggersByKey.get(triggerKey);
// does the trigger exist?
if (tw == null || tw.trigger == null) {
return;
}
// is the trigger in error state?
if (tw.state != TriggerWrapper.STATE_ERROR) {
return;
}

if(pausedTriggerGroups.contains(triggerKey.getGroup())) {
tw.state = TriggerWrapper.STATE_PAUSED;
}
else {
tw.state = TriggerWrapper.STATE_WAITING;
timeTriggers.add(tw);
}
}
}

/**
* <p>
* Store the given <code>{@link org.quartz.Calendar}</code>.
Expand Down
27 changes: 21 additions & 6 deletions quartz-core/src/main/java/org/quartz/spi/JobStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,21 @@ List<String> getCalendarNames()
*/
TriggerState getTriggerState(TriggerKey triggerKey) throws JobPersistenceException;

/**
* Reset the current state of the identified <code>{@link Trigger}</code>
* from {@link TriggerState#ERROR} to {@link TriggerState#NORMAL} or
* {@link TriggerState#PAUSED} as appropriate.
*
* <p>Only affects triggers that are in ERROR state - if identified trigger is not
* in that state then the result is a no-op.</p>
*
* <p>The result will be the trigger returning to the normal, waiting to
* be fired state, unless the trigger's group has been paused, in which
* case it will go into the PAUSED state.</p>
*/
void resetTriggerFromErrorState(TriggerKey triggerKey) throws JobPersistenceException;


/////////////////////////////////////////////////////////////////////////////
//
// Trigger State manipulation methods
Expand All @@ -449,7 +464,7 @@ List<String> getCalendarNames()
* paused.
* </p>
*
* @see #resumeTriggerGroup(String)
* @see #resumeTriggers(GroupMatcher)
*/
Collection<String> pauseTriggers(GroupMatcher<TriggerKey> matcher) throws JobPersistenceException;

Expand All @@ -471,7 +486,7 @@ List<String> getCalendarNames()
* paused.
* </p>
*
* @see #resumeJobGroup(String)
* @see #resumeJobs(GroupMatcher)
*/
Collection<String> pauseJobs(GroupMatcher<JobKey> groupMatcher)
throws JobPersistenceException;
Expand All @@ -498,7 +513,7 @@ Collection<String> pauseJobs(GroupMatcher<JobKey> groupMatcher)
* <code>Trigger</code>'s misfire instruction will be applied.
* </p>
*
* @see #pauseTriggers(String)
* @see #pauseTriggers(GroupMatcher)
*/
Collection<String> resumeTriggers(GroupMatcher<TriggerKey> matcher)
throws JobPersistenceException;
Expand Down Expand Up @@ -530,7 +545,7 @@ Set<String> getPausedTriggerGroups()
* misfire instruction will be applied.
* </p>
*
* @see #pauseJobGroup(String)
* @see #pauseJobs(GroupMatcher)
*/
Collection<String> resumeJobs(GroupMatcher<JobKey> matcher)
throws JobPersistenceException;
Expand All @@ -545,7 +560,7 @@ Collection<String> resumeJobs(GroupMatcher<JobKey> matcher)
* </p>
*
* @see #resumeAll()
* @see #pauseTriggers(String)
* @see #pauseTriggers(GroupMatcher)
*/
void pauseAll() throws JobPersistenceException;

Expand Down Expand Up @@ -576,7 +591,7 @@ void resumeAll()
* @param noLaterThan If > 0, the JobStore should only return a Trigger
* that will fire no later than the time represented in this value as
* milliseconds.
* @see #releaseAcquiredTrigger(Trigger)
* @see #releaseAcquiredTrigger(OperableTrigger)
*/
List<OperableTrigger> acquireNextTriggers(long noLaterThan, int maxCount, long timeWindow)
throws JobPersistenceException;
Expand Down
43 changes: 38 additions & 5 deletions quartz-core/src/test/java/org/quartz/AbstractJobStoreTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.quartz.simpl.CascadingClassLoadHelper;
import org.quartz.spi.ClassLoadHelper;
import org.quartz.spi.JobStore;
import org.quartz.spi.OperableTrigger;
import org.quartz.spi.SchedulerSignaler;
import org.quartz.spi.*;

import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.core.Is.is;
Expand Down Expand Up @@ -525,7 +522,43 @@ public void testAcquireTriggersInBatch() throws Exception {
Assert.assertEquals("job" + i, triggers.get(i).getKey().getName());
}
}


public void testResetErrorTrigger() throws Exception {

Date baseFireTimeDate = DateBuilder.evenMinuteDateAfterNow();
long baseFireTime = baseFireTimeDate.getTime();

// create and store a trigger
OperableTrigger trigger1 =
new SimpleTriggerImpl("trigger1", "triggerGroup1", this.fJobDetail.getName(),
this.fJobDetail.getGroup(), new Date(baseFireTime + 200000),
new Date(baseFireTime + 200000), 2, 2000);

trigger1.computeFirstFireTime(null);
this.fJobStore.storeTrigger(trigger1, false);

long firstFireTime = new Date(trigger1.getNextFireTime().getTime()).getTime();


// pretend to fire it
List<OperableTrigger> aqTs = this.fJobStore.acquireNextTriggers(
firstFireTime + 10000, 1, 0L);
assertEquals(trigger1.getKey(), aqTs.get(0).getKey());

List<TriggerFiredResult> fTs = this.fJobStore.triggersFired(aqTs);
TriggerFiredResult ft = fTs.get(0);

// get the trigger into error state
this.fJobStore.triggeredJobComplete(ft.getTriggerFiredBundle().getTrigger(), ft.getTriggerFiredBundle().getJobDetail(), Trigger.CompletedExecutionInstruction.SET_TRIGGER_ERROR);
TriggerState state = this.fJobStore.getTriggerState(trigger1.getKey());
assertEquals(TriggerState.ERROR, state);

// test reset
this.fJobStore.resetTriggerFromErrorState(trigger1.getKey());
state = this.fJobStore.getTriggerState(trigger1.getKey());
assertEquals(TriggerState.NORMAL, state);
}

public static class SampleSignaler implements SchedulerSignaler {
volatile int fMisfireCount = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ public Trigger.TriggerState getTriggerState(TriggerKey triggerKey) throws JobPer
}
}

@Override
public void resetTriggerFromErrorState(final TriggerKey triggerKey) throws JobPersistenceException {
try {
realJobStore.resetTriggerFromErrorState(triggerKey);
} catch (RejoinException e) {
throw new JobPersistenceException("Trigger state reset failed due to client rejoin", e);
}
}

@Override
public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) throws SchedulerConfigException {
init();
Expand Down
Loading

0 comments on commit b886159

Please sign in to comment.