/** * Returns the given {@code value} if it is non-{@code null}, otherwise a value produced * by the given {@code supplier} is returned instead. <p> * * Note: as the supplier is free to output {@code null}, this method can return a * {@code null} for a {@code null} input value. * * @param <T> Value type. * @param <X> Exception type. * @param value The value to consider. * @param supplier Supplier to invoke if the given value is {@code null}. * @return The given value (as-is) if it is non-{@code null}, or the value produced by the supplier otherwise. * @throws X If an error occurs inside the supplier. */ public static <T, X extends Throwable> T ifAbsent(T value, CheckedSupplier<? extends T, X> supplier) throws X { return value != null ? value : supplier.get(); }
/** * Obtains the underlying referent, instantiating it on the first request. * * @return The referent. * @throws X If an error occurs in the supplier. */ public T get() throws X { final T firstCheck = reference; if (firstCheck != null) { return firstCheck; } else { synchronized (lock) { final T secondCheck = reference; if (secondCheck != null) { return secondCheck; } else { final T created = supplier.get(); supplier = null; reference = created; return created; } } } }
/** * Attempts to return the value produced by the given {@code preferredSupplier} if this can be done * successfully without throwing an exception, otherwise the return value is sourced from the * {@code fallbackSupplier}. <p> * * The preferred supplier may throw a checked exception (which will be silently discarded). The * fallback supplier, on the other hand, is not permitted to throw a checked exception. * * @param <T> Value type. * @param preferredSupplier The supplier to attempt first. * @param fallbackSupplier The fallback supplier, if the preferred one fails. * @return The resulting value, sourced from either the preferred or the fallback supplier. */ public static <T> T ifThrew(CheckedSupplier<? extends T, ? extends Throwable> preferredSupplier, Supplier<? extends T> fallbackSupplier) { try { return preferredSupplier.get(); } catch (Throwable e) { return fallbackSupplier.get(); } }
/** * Functional variant of the try-catch statement that can return a value. * * @param <T> Return type. * @param errorProneSupplier A {@link CheckedSupplier} to try. * @param defaultValueSupplier If the supplier fails, the default supplier is used to return a value. * @param onError The {@link Throwable} {@link Consumer} that will handle any exceptions. * @return The resulting value. */ public static <T> T tryCatch(CheckedSupplier<? extends T, ?> errorProneSupplier, Supplier<? extends T> defaultValueSupplier, Consumer<Throwable> onError) { try { return errorProneSupplier.get(); } catch (Throwable e) { onError.accept(e); return defaultValueSupplier.get(); } }
/** * Applies a function to the result of a supplier, thereby creating a new supplier. * * @param <U> Original type supplied by {@code before} and input to the {@code after} function. * @param <V> Output type of the {@code after} function. * @param <X> Exception type. * @param before The original value supplier. * @param after The transformation to apply to the supplied value. * @return A composite {@link CheckedSupplier}. * @throws X If an error occurs. */ public static <U, V, X extends Throwable> CheckedSupplier<V, X> chain(CheckedSupplier<U, ? extends X> before, CheckedFunction<U, V, ? extends X> after) throws X { return () -> after.apply(before.get()); }
/** * A hybrid of {@link #ifPresent(Object, CheckedFunction)} and {@link #ifAbsent(Object, CheckedSupplier)}, * returning the application of the {@code mapperIfPresent} function to the given {@code value} if it * isn't {@code null}, or sourcing the value from the given {@code supplierIfAbsent} supplier if the * value is {@code null}. * * @param <T> Value type. * @param <U> Mapped or supplied type. * @param <X> Exception type. * @param value The value to consider. * @param mapperIfPresent The mapping function to invoke if the value is non-{@code null}. * @param supplierIfAbsent The supplier to invoke if the value is {@code null}. * @return The mapped value if the initial value isn't {@code null}, or the value produced by * the supplier otherwise. * @throws X If an error occurs inside either the mapping function or the supplier. */ public static <T, U, X extends Throwable> U ifEither(T value, CheckedFunction<? super T, ? extends U, X> mapperIfPresent, CheckedSupplier<? extends U, X> supplierIfAbsent) throws X { return value != null ? mapperIfPresent.apply(value) : supplierIfAbsent.get(); }
/** * A strict form of {@link #wrap(CheckedSupplier, Function)} that requires that the exception * wrapper takes a subtype of the exception thrown by the supplier. * * @param <T> Return type. * @param <W> Exception type thrown by the runnable, and input to the wrapper. * @param <X> Exception type thrown by the wrapper. * @param supplier The supplier whose exception to trap. * @param wrapper The handler for trapped exceptions, returning a wrapped exception of type {@code X}. * @return The result of evaluating the supplier. * @throws X If an exception occurs. */ public static <T, W extends Throwable, X extends Throwable> T wrapStrict(CheckedSupplier<? extends T, W> supplier, Function<? super W, ? extends X> wrapper) throws X { try { return supplier.get(); } catch (Throwable e) { throw wrapper.apply(Classes.cast(e)); } }
public <T, X extends Throwable> T run(CheckedSupplier<? extends T, X> operation) throws X { for (int attempt = 0;; attempt++) { try { return operation.get(); } catch (Throwable e) { if (exceptionMatcher.test(e)) { if (attempt == attempts - 1) { final String faultMessage = String.format("Fault (attempt #%,d of %,d): aborting", attempt + 1, attempts); errorHandler.onException(faultMessage, e); throw e; } else { final String retryMessage = String.format("Fault (attempt #%,d of %,d): retrying in %,d ms", attempt + 1, attempts, backoffMillis); faultHandler.onException(retryMessage, e); if (! Threads.sleep(backoffMillis)) { final String interruptMessage = String.format("Fault (attempt #%,d of %,d): aborting due to interrupt", attempt + 1, attempts); errorHandler.onException(interruptMessage, e); throw e; } } } else { throw e; } } } } }