Set timerIdsToRemove = new HashSet();
Set<TimerState> result = new HashSet<TimerState>();
for(TimerState timer: timersEligibleForRestoration) {
TimerPrimaryKey timerId = getPrimaryKey(timer);
if (getTimerState(timerId) != null) {
// Already restored. Add it to the result but do nothing else.
logger.log(Level.FINE, "@@@ Timer already restored: " + timer);
result.add(timer);
continue;
}
long containerId = timer.getContainerId();
// Timer might refer to an obsolete container.
BaseContainer container = getContainer(containerId);
if( container != null ) {
// Update applicationId if it is null (from previous version)
long appid = timer.getApplicationId();
if (appid == 0) {
timer.setApplicationId(container.getApplicationId());
}
// End update
Date initialExpiration = timer.getInitialExpiration();
// Create an instance of RuntimeTimerState.
// Only access timedObjectPrimaryKey if timed object is
// an entity bean. That allows us to lazily load the underlying
// blob for stateless session and message-driven bean timers.
Object timedObjectPrimaryKey = null;
if( container.getContainerType() == BaseContainer.ContainerType.ENTITY) {
timedObjectPrimaryKey = timer.getTimedObjectPrimaryKey();
}
RuntimeTimerState timerState = new RuntimeTimerState
(timerId, initialExpiration,
timer.getIntervalDuration(), container,
timedObjectPrimaryKey,
timer.getTimerSchedule(),
// Don't need to store the info ref for persistent timer
null, true);
timerCache_.addTimer(timerId, timerState);
// If a single-action timer is still in the database it never
// successfully delivered, so always reschedule a timer task
// for it. For periodic timers, we use the last known
// expiration time to decide whether we need to fire one
// ejbTimeout to make up for any missed ones.
Date expirationTime = initialExpiration;
Date now = new Date();
if( timerState.isPeriodic() ) {
// lastExpiration time, or null if we either aren't
// tracking last expiration or an expiration hasn't
// occurred yet for this timer.
Date lastExpiration = timer.getLastExpiration();
EJBTimerSchedule ts = timer.getTimerSchedule();
// @@@ need to handle case where last expiration time
// is not stored in database. This will be the case
// when we add configuration for update-db-on-delivery.
// However, for now assume we do update the db on each
// ejbTimeout. Therefore, if (lastExpirationTime == null),
// it means the timer didn't successfully complete any
// timer expirations.
if( (lastExpiration == null) &&
now.after(initialExpiration) ) {
if (!timerState.isExpired()) {
// This timer didn't even expire one time.
logger.log(Level.INFO,
"Rescheduling missed expiration for " +
"periodic timer " +
timerState + ". Timer expirations should " +
" have been delivered starting at " +
initialExpiration);
}
// keep expiration time at initialExpiration. That
// will force an ejbTimeout almost immediately. After
// that the timer will return to fixed rate expiration.
} else if ( (lastExpiration != null) &&
((ts != null && ts.getNextTimeout(lastExpiration).getTimeInMillis()
< now.getTime())
|| ((ts == null) && now.getTime() - lastExpiration.getTime()
> timer.getIntervalDuration()) ) ) {
// Schedule-based timer is periodic
logger.log(Level.INFO,
"Rescheduling missed expiration for " +
"periodic timer " +
timerState + ". Last timer expiration " +
"occurred at " + lastExpiration);
// Timer expired at least once and at least one
// missed expiration has occurred.
// keep expiration time at initialExpiration. That
// will force an ejbTimeout almost immediately. After
// that the timer will return to fixed rate expiration.
} else {
// In this case, at least one expiration has occurred
// but that was less than one period ago so there were
// no missed expirations.
expirationTime =
calcNextFixedRateExpiration(timerState);
}
} else { // single-action timer
if( now.after(initialExpiration) ) {
logger.log(Level.INFO,
"Rescheduling missed expiration for " +
"single-action timer " +
timerState + ". Timer expiration should " +
" have been delivered at " +
initialExpiration);
}
}
if (expirationTime == null) {
// Schedule-based timer will never expire again - remove it.
logger.log(Level.INFO,
"Removing schedule-based timer " + timerState +
" that will never expire again");
timerIdsToRemove.add(timerId);
} else {
timersToRestore.put(timerState, expirationTime);
result.add(timer);
}
} else {
// Timed object's container no longer exists - remember its id.
logger.log(Level.FINE,
"Skipping timer " + timerId +
" for container that is not up: " + containerId);
}
} // End -- for each active timer
if (timerIdsToRemove.size() > 0) {
timerLocal_.remove(timerIdsToRemove);
}
for(Iterator entries = timersToRestore.entrySet().iterator();
entries.hasNext(); ) {
Map.Entry next = (Map.Entry) entries.next();
RuntimeTimerState nextTimer = (RuntimeTimerState) next.getKey();
TimerPrimaryKey timerId = nextTimer.getTimerId();
Date expiration = (Date) next.getValue();
scheduleTask(timerId, expiration);
logger.log(Level.FINE,
"EJBTimerService.restoreTimers(), scheduling timer " +
nextTimer);