docs: @Override
      public Date getNextTimeout()
                      throws IllegalStateException,
                      NoSuchObjectLocalException,
                      EJBException, NoMoreTimeoutsException
      {
          final boolean isTraceOn = TraceComponent.isAnyTracingEnabled();
  
          if (isTraceOn && tc.isEntryEnabled())
              Tr.entry(tc, "getNextTimeout: " + this);
  
          // Determine if the calling bean is in a state that allows timer service
          // method access - throws IllegalStateException if not allowed.
          checkTimerAccess();
  
          // Determine if this timer is considered cancelled for the current
          // thread/transaction - throws NoSuchObjectLocalException if cancelled.
          checkIfCancelled();
  
          if (ivExpiration == 0) {
  
              if (ivParsedScheduleExpression != null) {
                  // NoMoreTimeoutsException is only thrown for calendar-based timers
                  // called from a timeout callback method.
                  if (ivTimeoutThread == Thread.currentThread()) {
                      String msg = "Timer with ID " + ivTaskId + " has no more timeouts.";
                      if (isTraceOn && tc.isEntryEnabled())
                          Tr.exit(tc, "getNextTimeout: NoMoreTimeoutsException : " + msg);
                      throw new NoMoreTimeoutsException(msg);
                  }
  
                  // Handle the rather odd scenario where a Timer is created that has
                  // no expirations. It exists, but NoMoreTimeoutsException is for
                  // use when called from a timeout callback.
                  String msg = "Timer with ID " + ivTaskId + " was created with no scheduled timeouts.";
                  if (isTraceOn && tc.isEntryEnabled())
                      Tr.exit(tc, "getNextTimeout: NoSuchObjectLocalException : no expirations : " + msg);
                  throw new NoSuchObjectLocalException(msg);
              }
  
              // If the expiration is 0 (and the timer hasn't been cancelled), then
              // the timer must be running, though this may or may not be the timer
              // callback thread. Either way, return the last expiration.
              //
              // For single action timers, for the timeout callback thread, the last
              // value is returned to be consistent with persistent timers, and that
              // is what is documented in info center.
              //
              // For non-timeout callback threads, the update to the expiration by
              // the timeout callback thread should not be visible yet, since
              // it hasn't committed, so also return the last expiration.
              Date nextTime = new Date(ivLastExpiration);
              if (isTraceOn && tc.isEntryEnabled())
                  Tr.exit(tc, "getNextTimeout: no timeouts : " + nextTime);
              return nextTime;
          }
  
          // If the timer is running, but this isn't the timeout callback thread
          // then the last expiration needs to be returned as the current thread
          // should not have visibility to the updated expiration just yet.
          if (ivTimeoutThread != null && ivTimeoutThread != Thread.currentThread()) {
              if (isTraceOn && tc.isEntryEnabled())
                  Tr.exit(tc, "getNextTimeout: non firing thread : " + new Date(ivLastExpiration));
              return new Date(ivLastExpiration);
          }
  
          Date nextTime = new Date(ivExpiration);
          if (isTraceOn && tc.isEntryEnabled())
              Tr.exit(tc, "getNextTimeout: " + nextTime);
          return nextTime;
      }