Groovy retry wrapper with capped exponential backoff

Groovy
import java.util.concurrent.ThreadLocalRandom

def retry(int maxAttempts, long baseMs, long capMs, Closure action) {
    for (int attempt = 1; ; attempt++) {
        try {
            return action()
        } catch (Exception e) {
            if (attempt >= maxAttempts) throw e
            long backoff = Math.min(capMs, baseMs * (1L << attempt))
            long jitter = (long) (ThreadLocalRandom.current().nextDouble(backoff * 0.5d))
            Thread.sleep(backoff / 2 + jitter)
        }
    }
}

println retry(3, 100, 2000) { 'done' }