RetrySchedule.java
package network.ike.plugin.release;
import org.apache.maven.api.plugin.Log;
import org.apache.maven.api.plugin.MojoException;
/**
* Backoff-schedule parsing and pre-retry sleep helpers shared by the
* Nexus and Central deploy retry loops.
*
* <p>Extracted from {@code ReleaseDraftMojo} during the Phase 4
* Commit 2 (IKE-Network/ike-issues#489) so that {@code NexusPhase}
* and {@code CentralPhase} can share a single canonical
* implementation instead of either duplicating the logic or depending
* back on the mojo.
*
* <p>Both methods are pure utilities with no instance state.
*/
public final class RetrySchedule {
private RetrySchedule() {
}
/**
* Parses a comma-separated list of non-negative integer seconds
* into a backoff schedule array.
*
* <p>Accepts whitespace between entries. Each entry must parse as
* a non-negative integer; zero is permitted (no sleep). The
* resulting array has one entry per configured cycle gap, so a
* schedule of {@code "60,300,900"} produces backoff
* {@code {60, 300, 900}} — applied as the wait before retry
* cycles 2, 3, and 4 respectively.
*
* @param property property name surfaced in error messages (e.g. {@code "ike.deploy.nexus.backoffSeconds"})
* @param csv the raw comma-separated value
* @return parsed seconds array (length >= 1)
* @throws MojoException if {@code csv} is null or blank, an entry is non-integer, or an entry is negative
*/
public static int[] parseSeconds(String property, String csv) {
if (csv == null || csv.isBlank()) {
throw new MojoException(property
+ " must be a non-empty comma-separated list "
+ "of non-negative integer seconds");
}
String[] parts = csv.split(",");
int[] arr = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
String trimmed = parts[i].trim();
int value;
try {
value = Integer.parseInt(trimmed);
} catch (NumberFormatException e) {
throw new MojoException("Invalid " + property
+ " entry '" + trimmed + "' — expected "
+ "comma-separated non-negative integers "
+ "(seconds)", e);
}
if (value < 0) {
throw new MojoException(property
+ " entries must be >= 0: " + trimmed);
}
arr[i] = value;
}
return arr;
}
/**
* Sleeps before the next retry cycle, with a clear log line so
* the operator knows the build isn't hung.
*
* <p>A {@code seconds} value of zero or less is a no-op (no sleep,
* no log). On thread interrupt, restores the interrupt flag and
* throws a {@link MojoException}.
*
* @param log logger for the wait-announcement line
* @param seconds seconds to wait (0 = no sleep)
* @param label phase label ({@code "Nexus deploy"} / {@code "Maven Central deploy"})
* @param nextAttempt the upcoming cycle number
* @param maxAttempts configured max cycles
*/
public static void sleepBefore(Log log, int seconds, String label,
int nextAttempt, int maxAttempts) {
if (seconds <= 0) {
return;
}
log.info("Waiting " + seconds + "s before " + label
+ " cycle " + nextAttempt + "/" + maxAttempts
+ "...");
try {
Thread.sleep(seconds * 1000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new MojoException("Interrupted while waiting to "
+ "retry " + label, e);
}
}
}