mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
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:
@@ -205,36 +205,116 @@ public class ScheduledPersistedActionImpl implements ScheduledPersistedAction
|
|||||||
return null;
|
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 they don't want it to repeat, just use a simple interval
|
||||||
if(getScheduleInterval() == null)
|
if(getScheduleInterval() == null)
|
||||||
{
|
{
|
||||||
SimpleTrigger trigger = new SimpleTrigger(
|
SimpleTrigger trigger = new SimpleTrigger(
|
||||||
triggerName, null,
|
triggerName, null,
|
||||||
scheduleStart
|
startAt
|
||||||
);
|
);
|
||||||
trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
|
trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
|
||||||
return trigger;
|
return trigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return buildDateIntervalTrigger(triggerName, startAt, endAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateIntervalTrigger buildDateIntervalTrigger(String triggerName, Date startAt, Date endAt)
|
||||||
|
{
|
||||||
// There are some repeating rules
|
// There are some repeating rules
|
||||||
// Create a Date Interval trigger
|
// Create a Date Interval trigger
|
||||||
DateIntervalTrigger.IntervalUnit quartzInterval =
|
DateIntervalTrigger.IntervalUnit quartzInterval =
|
||||||
DateIntervalTrigger.IntervalUnit.valueOf(
|
DateIntervalTrigger.IntervalUnit.valueOf(
|
||||||
intervalPeriod.toString().toUpperCase()
|
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
|
// Create the interval
|
||||||
DateIntervalTrigger trigger = new DateIntervalTrigger(
|
DateIntervalTrigger trigger = new DateIntervalTrigger(
|
||||||
triggerName, null,
|
triggerName, null,
|
||||||
start, end,
|
startAt, endAt,
|
||||||
quartzInterval, intervalCount
|
quartzInterval, intervalCount
|
||||||
);
|
);
|
||||||
trigger.setMisfireInstruction( DateIntervalTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW );
|
trigger.setMisfireInstruction( DateIntervalTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW );
|
||||||
|
@@ -33,7 +33,9 @@ import org.alfresco.repo.action.RuntimeActionService;
|
|||||||
import org.alfresco.repo.model.Repository;
|
import org.alfresco.repo.model.Repository;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
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.RetryingTransactionHelper;
|
||||||
|
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.cmr.action.Action;
|
import org.alfresco.service.cmr.action.Action;
|
||||||
import org.alfresco.service.cmr.action.ActionService;
|
import org.alfresco.service.cmr.action.ActionService;
|
||||||
@@ -391,24 +393,36 @@ 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)
|
protected void addToScheduler(ScheduledPersistedActionImpl schedule)
|
||||||
{
|
{
|
||||||
// Wrap it up in Quartz bits
|
// Wrap it up in Quartz bits
|
||||||
JobDetail details = buildJobDetail(schedule);
|
final JobDetail details = buildJobDetail(schedule);
|
||||||
Trigger trigger = schedule.asTrigger();
|
final Trigger trigger = schedule.asTrigger();
|
||||||
|
|
||||||
// Schedule it
|
// As soon as the transaction commits, add it
|
||||||
try
|
AlfrescoTransactionSupport.bindListener(
|
||||||
{
|
new TransactionListenerAdapter() {
|
||||||
scheduler.scheduleJob(details, trigger);
|
@Override
|
||||||
}
|
public void afterCommit() {
|
||||||
catch (SchedulerException e)
|
// Schedule it with Quartz
|
||||||
{
|
try
|
||||||
// Probably means scheduler is shutting down
|
{
|
||||||
log.warn(e);
|
scheduler.scheduleJob(details, trigger);
|
||||||
}
|
}
|
||||||
|
catch (SchedulerException e)
|
||||||
|
{
|
||||||
|
// Probably means scheduler is shutting down
|
||||||
|
log.warn(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected JobDetail buildJobDetail(ScheduledPersistedActionImpl schedule)
|
protected JobDetail buildJobDetail(ScheduledPersistedActionImpl schedule)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user