class JedisLockProvider implements ExtensibleLockProvider {

    private static final String KEY_PREFIX = "job-lock";
    private static final String ENV_DEFAULT = "default";

    private final JedisTemplate jedisTemplate;
    private final String environment;

    public JedisLockProvider(@NonNull Pool<Jedis> jedisPool) {
        this(jedisPool, ENV_DEFAULT);
    }

    /**
     * Creates JedisLockProvider
     *
     * @param jedisPool
     *            Jedis connection pool
     * @param environment
     *            environment is part of the key and thus makes sure there is not
     *            key conflict between multiple ShedLock instances running on the
     *            same Redis
     */
    public JedisLockProvider(@NonNull Pool<Jedis> jedisPool, @NonNull String environment) {
        this.jedisTemplate = new JedisPoolTemplate(jedisPool);
        this.environment = environment;
    }

    /**
     * Creates JedisLockProvider
     *
     * @param jedisCommands
     *            implementation of JedisCommands.
     * @param environment
     *            environment is part of the key and thus makes sure there is not
     *            key conflict between multiple ShedLock instances running on the
     *            same Redis
     */
    public JedisLockProvider(@NonNull JedisCommands jedisCommands, @NonNull String environment) {
        this.jedisTemplate = new JedisCommandsTemplate(jedisCommands);
        this.environment = environment;
    }

    @Override
    @NonNull
    public Optional<SimpleLock> lock(@NonNull LockConfiguration lockConfiguration) {<FILL_FUNCTION_BODY>}

    private Optional<SimpleLock> extend(LockConfiguration lockConfiguration) {
        long expireTime = getMsUntil(lockConfiguration.getLockAtMostUntil());

        String key = buildKey(lockConfiguration.getName(), this.environment);

        String rez = extendKeyExpiration(key, expireTime);

        if ("OK".equals(rez)) {
            return Optional.of(new RedisLock(key, this, lockConfiguration));
        }

        return Optional.empty();
    }

    private String extendKeyExpiration(String key, long expiration) {
        return jedisTemplate.set(key, buildValue(), setParams().xx().px(expiration));
    }

    private void deleteKey(String key) {
        jedisTemplate.del(key);
    }

    private static final class RedisLock extends AbstractSimpleLock {
        private final String key;
        private final JedisLockProvider jedisLockProvider;

        private RedisLock(String key, JedisLockProvider jedisLockProvider, LockConfiguration lockConfiguration) {
            super(lockConfiguration);
            this.key = key;
            this.jedisLockProvider = jedisLockProvider;
        }

        @Override
        public void doUnlock() {
            long keepLockFor = getMsUntil(lockConfiguration.getLockAtLeastUntil());

            // lock at least until is in the past
            if (keepLockFor <= 0) {
                try {
                    jedisLockProvider.deleteKey(key);
                } catch (Exception e) {
                    throw new LockException("Can not remove node", e);
                }
            } else {
                jedisLockProvider.extendKeyExpiration(key, keepLockFor);
            }
        }

        @Override
        @NonNull
        protected Optional<SimpleLock> doExtend(@NonNull LockConfiguration newConfiguration) {
            return jedisLockProvider.extend(newConfiguration);
        }
    }

    private static long getMsUntil(Instant instant) {
        return Duration.between(ClockProvider.now(), instant).toMillis();
    }

    static String buildKey(String lockName, String env) {
        return String.format("%s:%s:%s", KEY_PREFIX, env, lockName);
    }

    private static String buildValue() {
        return String.format("ADDED:%s@%s", toIsoString(ClockProvider.now()), getHostname());
    }

    private interface JedisTemplate {
        String set(String key, String value, SetParams setParams);

        void del(String key);
    }

    private record JedisPoolTemplate(Pool<Jedis> jedisPool) implements JedisTemplate {

        @Override
        public String set(String key, String value, SetParams setParams) {
            try (Jedis jedis = jedisPool.getResource()) {
                return jedis.set(key, value, setParams);
            }
        }

        @Override
        public void del(String key) {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.del(key);
            }
        }
    }

    private record JedisCommandsTemplate(JedisCommands jedisCommands) implements JedisTemplate {

        @Override
        public String set(String key, String value, SetParams setParams) {
            return jedisCommands.set(key, value, setParams);
        }

        @Override
        public void del(String key) {
            jedisCommands.del(key);
        }
    }
}

        long expireTime = getMsUntil(lockConfiguration.getLockAtMostUntil());

        String key = buildKey(lockConfiguration.getName(), this.environment);

        String rez = jedisTemplate.set(key, buildValue(), setParams().nx().px(expireTime));

        if ("OK".equals(rez)) {
            return Optional.of(new RedisLock(key, this, lockConfiguration));
        }

        return Optional.empty();
    
<no_super_class>