ejb3/src/main/java/org/jboss/as/ejb3/timerservice/TimerTask.java
@Override
    public void run() {
        ClassLoader old = WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(getClass());
        try {
            final TimerImpl timer = timerService.getTimer(timerId);
            try {
                if (cancelled) {
                    EJB3_TIMER_LOGGER.debugf("Timer task was cancelled for %s", timer);
                    return;
                }

                Date now = new Date();
                EJB3_TIMER_LOGGER.debugf("Timer task invoked at: %s for timer %s", now, timer);

                //we lock the timer for this check, because if a cancel is in progress or a running timeout is about to finish
                //and try to update the timer state after finish the inTimeout method,
                //we do not want to do the isActive check, but wait for the other transaction to finish
                //one way or another
                timer.lock();

                try {
                    // If a retry thread is in progress, we don't want to allow another
                    // interval to execute until the retry is complete. See JIRA-1926.
                    if (timer.isInRetry()) {
                        EJB3_TIMER_LOGGER.skipInvokeTimeoutDuringRetry(timer, now);
                        // compute the next timeout, See JIRA AS7-2995.
                        timer.setNextTimeout(calculateNextTimeout(timer));
                        timerService.persistTimer(timer, false);
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    // Check whether the timer is running local
                    // If the recurring timer running longer than the interval is, we don't want to allow another
                    // execution until it is complete. See JIRA AS7-3119
                    if (timer.getState() == TimerState.IN_TIMEOUT || timer.getState() == TimerState.RETRY_TIMEOUT) {
                        EJB3_TIMER_LOGGER.skipOverlappingInvokeTimeout(timer, now);

                        if (EJB3_TIMER_LOGGER.isDebugEnabled()) {
                            // WFLY-10542 log thread stack trace which is processing timer task in debug level to diagnose timer overlap
                            Thread otherThread = timer.getExecutingThread();
                            // can be null for clustered timers if the timer is executing on another node.
                            if (otherThread != null) {
                                final StringBuilder debugMsg = new StringBuilder()
                                        .append("Thread: ")
                                        .append(otherThread.getName())
                                        .append(" Id: ")
                                        .append(otherThread.getId())
                                        .append(" of group ")
                                        .append(otherThread.getThreadGroup())
                                        .append(" is in state: ")
                                        .append(otherThread.getState());
                                for (StackTraceElement ste : otherThread.getStackTrace()) {
                                    debugMsg.append(System.lineSeparator()).append(ste.toString());
                                }
                                EJB3_TIMER_LOGGER.debugf(debugMsg.toString());
                            }
                        }

                        Date newD = this.calculateNextTimeout(timer);
                        timer.setNextTimeout(newD);
                        timerService.persistTimer(timer, false);
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    // Check whether we want to run the timer
                    if (!timerService.shouldRun(timer)) {
                        EJB3_TIMER_LOGGER.debugf("Skipping execution of timer for %s as it is being run on another node or the execution is suppressed by configuration", timer.getTimedObjectId());
                        timer.setNextTimeout(calculateNextTimeout(timer));
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    if (!timer.isActive()) {
                        EJB3_TIMER_LOGGER.debug("Timer is not active, skipping this scheduled execution at: " + now + "for " + timer);
                        return;
                    }
                    // set the current date as the "previous run" of the timer.
                    timer.setPreviousRun(new Date());
                    Date nextTimeout = this.calculateNextTimeout(timer);
                    timer.setNextTimeout(nextTimeout);
                    // change the state to mark it as in timeout method
                    timer.setTimerState(TimerState.IN_TIMEOUT, Thread.currentThread());

                    // persist changes
                    timerService.persistTimer(timer, false);

                } finally {
                    timer.unlock();
                }
                try {
                    // invoke timeout
                    this.callTimeout(timer);
                } catch (Exception e) {
                    EJB3_TIMER_LOGGER.errorInvokeTimeout(timer, e);
                    try {
                        EJB3_TIMER_LOGGER.timerRetried(timer);
                        retryTimeout(timer);
                    } catch (Exception retryException) {
                        // that's it, we can't do anything more. Let's just log the exception
                        // and return
                        EJB3_TIMER_LOGGER.errorDuringRetryTimeout(timer, retryException);
                    }
                } finally {
                    this.postTimeoutProcessing(timer);
                }
            } catch (Exception e) {
                EJB3_TIMER_LOGGER.exceptionRunningTimerTask(timer, timedObjectId, e);
            }
        } finally {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(old);
        }
    }
@Override
    public void run() {
        ClassLoader old = WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(getClass());
        try {
            final TimerImpl timer = timerService.getTimer(timerId);
            try {
                if (cancelled) {
                    EJB3_TIMER_LOGGER.debugf("Timer task was cancelled for %s", timer);
                    return;
                }

                Date now = new Date();
                EJB3_TIMER_LOGGER.debugf("Timer task invoked at: %s for timer %s", now, timer);

                //we lock the timer for this check, because if a cancel is in progress or a running timeout is about to finish
                //and try to update the timer state after finish the inTimeout method,
                //we do not want to do the isActive check, but wait for the other transaction to finish
                //one way or another
                timer.lock();

                try {
                    // If a retry thread is in progress, we don't want to allow another
                    // interval to execute until the retry is complete. See JIRA-1926.
                    if (timer.isInRetry()) {
                        EJB3_TIMER_LOGGER.skipInvokeTimeoutDuringRetry(timer, now);
                        // compute the next timeout, See JIRA AS7-2995.
                        timer.setNextTimeout(calculateNextTimeout(timer));
                        timerService.persistTimer(timer, false);
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    // Check whether the timer is running local
                    // If the recurring timer running longer than the interval is, we don't want to allow another
                    // execution until it is complete. See JIRA AS7-3119
                    if (timer.getState() == TimerState.IN_TIMEOUT || timer.getState() == TimerState.RETRY_TIMEOUT) {
                        EJB3_TIMER_LOGGER.skipOverlappingInvokeTimeout(timer, now);

                        if (EJB3_TIMER_LOGGER.isDebugEnabled()) {
                            // WFLY-10542 log thread stack trace which is processing timer task in debug level to diagnose timer overlap
                            Thread otherThread = timer.getExecutingThread();
                            // can be null for clustered timers if the timer is executing on another node.
                            if (otherThread != null) {
                                final StringBuilder debugMsg = new StringBuilder()
                                        .append("Thread: ")
                                        .append(otherThread.getName())
                                        .append(" Id: ")
                                        .append(otherThread.getId())
                                        .append(" of group ")
                                        .append(otherThread.getThreadGroup())
                                        .append(" is in state: ")
                                        .append(otherThread.getState());
                                for (StackTraceElement ste : otherThread.getStackTrace()) {
                                    debugMsg.append(System.lineSeparator()).append(ste.toString());
                                }
                                EJB3_TIMER_LOGGER.debugf(debugMsg.toString());
                            }
                        }

                        Date newD = this.calculateNextTimeout(timer);
                        timer.setNextTimeout(newD);
                        timerService.persistTimer(timer, false);
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    // Check whether we want to run the timer
                    if (!timerService.shouldRun(timer)) {
                        EJB3_TIMER_LOGGER.debugf("Skipping execution of timer for %s as it is being run on another node or the execution is suppressed by configuration", timer.getTimedObjectId());
                        timer.setNextTimeout(calculateNextTimeout(timer));
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    if (!timer.isActive()) {
                        EJB3_TIMER_LOGGER.debug("Timer is not active, skipping this scheduled execution at: " + now + "for " + timer);
                        return;
                    }
                    // set the current date as the "previous run" of the timer.
                    timer.setPreviousRun(new Date());
                    Date nextTimeout = this.calculateNextTimeout(timer);
                    timer.setNextTimeout(nextTimeout);
                    // change the state to mark it as in timeout method
                    timer.setTimerState(TimerState.IN_TIMEOUT, Thread.currentThread());

                    // persist changes
                    timerService.persistTimer(timer, false);

                } finally {
                    timer.unlock();
                }
                try {
                    // invoke timeout
                    this.callTimeout(timer);
                } catch (Exception e) {
                    EJB3_TIMER_LOGGER.errorInvokeTimeout(timer, e);
                    try {
                        EJB3_TIMER_LOGGER.timerRetried(timer);
                        retryTimeout(timer);
                    } catch (Exception retryException) {
                        // that's it, we can't do anything more. Let's just log the exception
                        // and return
                        EJB3_TIMER_LOGGER.errorDuringRetryTimeout(timer, retryException);
                    }
                } finally {
                    this.postTimeoutProcessing(timer);
                }
            } catch (Exception e) {
                EJB3_TIMER_LOGGER.exceptionRunningTimerTask(timer, timedObjectId, e);
            }
        } finally {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(old);
        }
    }
Invokes the timeout method through the {@link org.jboss.as.ejb3.timerservice.spi.TimedObjectInvoker} corresponding
to the {@link org.jboss.as.ejb3.timerservice.TimerImpl} to which this {@link TimerTask} belongs.
<p>
This method also sets other attributes on the {@link org.jboss.as.ejb3.timerservice.TimerImpl} including the
next timeout of the timer and the timer state.
</p>
<p>
Additionally, for calendar timers, this method even schedules the next timeout timer task
before calling the timeout method for the current timeout.
</p>