All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.shredzone.acme4j.RenewalInfo Maven / Gradle / Ivy

The newest version!
/*
 * acme4j - Java ACME client
 *
 * Copyright (C) 2023 Richard "Shred" Körber
 *   http://acme4j.shredzone.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.shredzone.acme4j;

import java.net.URL;
import java.time.Instant;
import java.time.temporal.TemporalAmount;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;

import edu.umd.cs.findbugs.annotations.Nullable;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Renewal Information of a certificate.
 *
 * @draft This class is currently based on an RFC draft. It may be changed or
 * removed without notice to reflect future changes to the draft. SemVer rules
 * do not apply here.
 * @since 3.0.0
 */
public class RenewalInfo extends AcmeJsonResource {
    private static final Logger LOG = LoggerFactory.getLogger(RenewalInfo.class);

    protected RenewalInfo(Login login, URL location) {
        super(login, location);
    }

    /**
     * Returns the starting {@link Instant} of the time window the CA recommends for
     * certificate renewal.
     */
    public Instant getSuggestedWindowStart() {
        return getJSON().get("suggestedWindow").asObject().get("start").asInstant();
    }

    /**
     * Returns the ending {@link Instant} of the time window the CA recommends for
     * certificate renewal.
     */
    public Instant getSuggestedWindowEnd() {
        return getJSON().get("suggestedWindow").asObject().get("end").asInstant();
    }

    /**
     * An optional {@link URL} pointing to a page which may explain why the suggested
     * renewal window is what it is.
     */
    public Optional getExplanation() {
        return getJSON().get("explanationURL").optional().map(Value::asURL);
    }

    /**
     * Checks if the given {@link Instant} is before the suggested time window, so a
     * certificate renewal is not required yet.
     *
     * @param instant
     *         {@link Instant} to check
     * @return {@code true} if the {@link Instant} is before the time window, {@code
     * false} otherwise.
     */
    public boolean renewalIsNotRequired(Instant instant) {
        assertValidTimeWindow();
        return instant.isBefore(getSuggestedWindowStart());
    }

    /**
     * Checks if the given {@link Instant} is within the suggested time window, and a
     * certificate renewal is recommended.
     * 

* An {@link Instant} is deemed to be within the time window if it is equal to, or * after {@link #getSuggestedWindowStart()}, and before {@link * #getSuggestedWindowEnd()}. * * @param instant * {@link Instant} to check * @return {@code true} if the {@link Instant} is within the time window, {@code * false} otherwise. */ public boolean renewalIsRecommended(Instant instant) { assertValidTimeWindow(); return !instant.isBefore(getSuggestedWindowStart()) && instant.isBefore(getSuggestedWindowEnd()); } /** * Checks if the given {@link Instant} is past the time window, and a certificate * renewal is overdue. *

* An {@link Instant} is deemed to be past the time window if it is equal to, or after * {@link #getSuggestedWindowEnd()}. * * @param instant * {@link Instant} to check * @return {@code true} if the {@link Instant} is past the time window, {@code false} * otherwise. */ public boolean renewalIsOverdue(Instant instant) { assertValidTimeWindow(); return !instant.isBefore(getSuggestedWindowEnd()); } /** * Returns a proposed {@link Instant} when the certificate related to this * {@link RenewalInfo} should be renewed. *

* This method is useful for setting alarms for renewal cron jobs. As a parameter, the * frequency of the cron job is set. The resulting {@link Instant} is guaranteed to be * executed in time, considering the cron job intervals. *

* This method uses {@link ThreadLocalRandom} for random numbers. It is sufficient for * most cases, as only an "earliest" {@link Instant} is returned, but the actual * renewal process also depends on cron job execution times and other factors like * system load. *

* The result is empty if it is impossible to renew the certificate in time, under the * given circumstances. This is either because the time window already ended in the * past, or because the cron job would not be executed before the ending of the time * window. In this case, it is recommended to renew the certificate immediately. * * @param frequency * Frequency of the cron job executing the certificate renewals. May be * {@code null} if there is no cron job, and the renewal is going to be * executed exactly at the given {@link Instant}. * @return Random {@link Instant} when the certificate should be renewed. This instant * might be slightly in the past. In this case, start the renewal process at the next * possible regular moment. */ public Optional getRandomProposal(@Nullable TemporalAmount frequency) { assertValidTimeWindow(); Instant start = Instant.now(); Instant suggestedStart = getSuggestedWindowStart(); if (start.isBefore(suggestedStart)) { start = suggestedStart; } Instant end = getSuggestedWindowEnd(); if (frequency != null) { end = end.minus(frequency); } if (!end.isAfter(start)) { return Optional.empty(); } return Optional.of(Instant.ofEpochMilli(ThreadLocalRandom.current().nextLong( start.toEpochMilli(), end.toEpochMilli()))); } /** * Asserts that the end of the suggested time window is after the start. */ private void assertValidTimeWindow() { if (getSuggestedWindowStart().isAfter(getSuggestedWindowEnd())) { throw new AcmeProtocolException("Received an invalid suggested window"); } } @Override public Optional fetch() throws AcmeException { LOG.debug("update RenewalInfo"); try (Connection conn = getSession().connect()) { conn.sendRequest(getLocation(), getSession(), null); setJSON(conn.readJsonResponse()); var retryAfterOpt = conn.getRetryAfter(); retryAfterOpt.ifPresent(instant -> LOG.debug("Retry-After: {}", instant)); setRetryAfter(retryAfterOpt.orElse(null)); return retryAfterOpt; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy