class KeepAliveLock extends AbstractSimpleLock {
        private final Duration lockExtensionPeriod;
        private SimpleLock lock;
        private Duration remainingLockAtLeastFor;
        private final ScheduledFuture<?> future;
        private boolean active = true;
        private Instant currentLockAtMostUntil;

        private KeepAliveLock(
                LockConfiguration lockConfiguration, SimpleLock lock, ScheduledExecutorService executorService) {
            super(lockConfiguration);
            this.lock = lock;
            this.lockExtensionPeriod = lockConfiguration.getLockAtMostFor().dividedBy(2);
            this.remainingLockAtLeastFor = lockConfiguration.getLockAtLeastFor();
            this.currentLockAtMostUntil = lockConfiguration.getLockAtMostUntil();

            long extensionPeriodMs = lockExtensionPeriod.toMillis();
            this.future = executorService.scheduleAtFixedRate(
                    this::extendForNextPeriod, extensionPeriodMs, extensionPeriodMs, MILLISECONDS);
        }

        private void extendForNextPeriod() {<FILL_FUNCTION_BODY>}

        private void stop() {
            active = false;
            future.cancel(false);
        }

        @Override
        protected void doUnlock() {
            synchronized (this) {
                logger.trace("Unlocking lock {}", lockConfiguration.getName());
                stop();
                lock.unlock();
            }
        }

        @Override
        protected Optional<SimpleLock> doExtend(LockConfiguration newConfiguration) {
            throw new UnsupportedOperationException("Manual extension of KeepAliveLock is not supported (yet)");
        }
    }

            // We can have a race-condition when we extend the lock but the `lock` field is
            // accessed
            // before we update it.
            synchronized (this) {
                if (!active) {
                    return;
                }
                if (currentLockAtMostUntil.isBefore(now())) {
                    // Failsafe for cases when we are not able to extend the lock and it expires
                    // before the
                    // extension
                    // In such case someone else might have already obtained the lock so we can't
                    // extend it.
                    stop();
                    return;
                }
                remainingLockAtLeastFor = remainingLockAtLeastFor.minus(lockExtensionPeriod);
                if (remainingLockAtLeastFor.isNegative()) {
                    remainingLockAtLeastFor = Duration.ZERO;
                }
                currentLockAtMostUntil = now().plus(lockConfiguration.getLockAtMostFor());
                Optional<SimpleLock> extendedLock =
                        lock.extend(lockConfiguration.getLockAtMostFor(), remainingLockAtLeastFor);
                if (extendedLock.isPresent()) {
                    lock = extendedLock.get();
                    logger.trace(
                            "Lock {} extended for {}",
                            lockConfiguration.getName(),
                            lockConfiguration.getLockAtMostFor());
                } else {
                    logger.warn("Can't extend lock {}", lockConfiguration.getName());
                    stop();
                }
            }
        
<no_super_class>