Improve how scheduled actions with historic start dates are handled during startup. The last run time is used to decide when they need to be re-scheduled for (ALF-4505)

Also, don't add the new scheduled action to Quartz until the transaction commits, to ensure the nodes are really there before Quartz fires


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@22136 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Nick Burch
2010-09-01 16:16:32 +00:00
parent 73ae4f71ca
commit 1f5c3a1d20
3 changed files with 1298 additions and 841 deletions

View File

@@ -205,36 +205,116 @@ public class ScheduledPersistedActionImpl implements ScheduledPersistedAction
return null;
}
// If the end date is in the past, don't schedule
if(scheduleEnd != null && scheduleEnd.getTime() < System.currentTimeMillis())
{
return null;
}
// Decide what to use as the start time
// This will be based on the requested start time, when
// the schedule last ran (if ever), and the interval
Date startAt = scheduleStart;
Date endAt = scheduleEnd;
if(startAt == null)
{
// No start time specified, so start immediately
startAt = new Date();
}
else
{
// A start time was given. Is it still to come?
if(startAt.getTime() >= System.currentTimeMillis())
{
// The start date hasn't happened yet
// This means we can just use it as-is
}
else
{
// The start date has passed. Have we ever run this?
if(lastExecutedAt == null)
{
// The schedule has never run
// Tell Quartz the requested start time, so it runs
// immediately, and repeats are correctly calculated
startAt = scheduleStart;
}
else if(lastExecutedAt.getTime() < startAt.getTime())
{
// The previous run time is before the scheduled
// start time, so has probably been edited, and
// both of these dates is in the past.
// Tell Quartz the requested start time, so it runs
// immediately, and repeats are correctly calculated
startAt = scheduleStart;
}
else
{
// The start date is in the past, and we have
// previously run the job
if(getScheduleInterval() == null)
{
// There is no schedule setup, so this is
// probably just an old job on startup/edit
// So, don't run it
return null;
}
// Based on the start time, when would it next be
// due to fire?
DateIntervalTrigger testT = buildDateIntervalTrigger("TEST", scheduleStart, null);
Date nextFireFromNow = testT.getFireTimeAfter(new Date());
Date nextFireFromLast = testT.getFireTimeAfter(lastExecutedAt);
// If the next fire time from the last is before the
// next due date, then we missed one
if(nextFireFromLast.getTime() < nextFireFromNow.getTime())
{
// We missed one!
// Tell Quartz the requested start time, so it runs
// immediately, and repeats are correctly calculated
startAt = scheduleStart;
}
else
{
// The last run time was largely when due
// So, don't run until the next time
startAt = nextFireFromNow;
}
}
}
}
// If they don't want it to repeat, just use a simple interval
if(getScheduleInterval() == null)
{
SimpleTrigger trigger = new SimpleTrigger(
triggerName, null,
scheduleStart
startAt
);
trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
return trigger;
}
return buildDateIntervalTrigger(triggerName, startAt, endAt);
}
private DateIntervalTrigger buildDateIntervalTrigger(String triggerName, Date startAt, Date endAt)
{
// There are some repeating rules
// Create a Date Interval trigger
DateIntervalTrigger.IntervalUnit quartzInterval =
DateIntervalTrigger.IntervalUnit.valueOf(
intervalPeriod.toString().toUpperCase()
);
// Ensure we always have a start date, even if it's now
Date start = scheduleStart;
if(start == null)
start = new Date();
// The end date is allowed to be null
Date end = scheduleEnd;
// Create the interval
DateIntervalTrigger trigger = new DateIntervalTrigger(
triggerName, null,
start, end,
startAt, endAt,
quartzInterval, intervalCount
);
trigger.setMisfireInstruction( DateIntervalTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW );

View File

@@ -33,7 +33,9 @@ import org.alfresco.repo.action.RuntimeActionService;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
@@ -391,15 +393,24 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc
}
/**
* Adds a new entry into the scheduler
* Builds up the Quartz details, and adds it to the Quartz
* scheduler when the transaction completes.
* We have to wait for the transaction to finish, otherwise
* Quartz may end up trying and failing to load the details
* of a job that hasn't been committed to the repo yet!
*/
protected void addToScheduler(ScheduledPersistedActionImpl schedule)
{
// Wrap it up in Quartz bits
JobDetail details = buildJobDetail(schedule);
Trigger trigger = schedule.asTrigger();
final JobDetail details = buildJobDetail(schedule);
final Trigger trigger = schedule.asTrigger();
// Schedule it
// As soon as the transaction commits, add it
AlfrescoTransactionSupport.bindListener(
new TransactionListenerAdapter() {
@Override
public void afterCommit() {
// Schedule it with Quartz
try
{
scheduler.scheduleJob(details, trigger);
@@ -410,6 +421,9 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc
log.warn(e);
}
}
}
);
}
protected JobDetail buildJobDetail(ScheduledPersistedActionImpl schedule)
{