diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Preconditions.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Preconditions.java index 0cded9a5e6..4b98797df3 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Preconditions.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Preconditions.java @@ -46,6 +46,8 @@ public final class Preconditions { "The argument object is NULL"; private static final String CHECK_ARGUMENT_EX_MESSAGE = "The argument expression is false"; + private static final String CHECK_STATE_EX_MESSAGE = + "The state expression is false"; private Preconditions() { } @@ -240,6 +242,91 @@ public static void checkArgument( } } + /** + * Ensures the truth of an expression involving the state of the calling instance + * without involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(final boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance + * without involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(final boolean expression, final Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance + * without involving any parameters to the calling method. + * + *

The message of the exception is {@code String.format(f, m)}.

+ * + * @param expression a boolean expression + * @param errorMsg the {@link String#format(String, Object...)} + * exception message if valid. Otherwise, + * the message is {@link #CHECK_STATE_EX_MESSAGE} + * @param errorMsgArgs the optional values for the formatted exception message. + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState( + final boolean expression, + final String errorMsg, + Object... errorMsgArgs) { + if (!expression) { + String msg; + try { + msg = String.format(errorMsg, errorMsgArgs); + } catch (Exception e) { + LOG.debug("Error formatting message", e); + msg = CHECK_STATE_EX_MESSAGE; + } + throw new IllegalStateException(msg); + } + } + + /** + * Preconditions that the expression involving one or more parameters to the calling method. + * + *

The message of the exception is {@code msgSupplier.get()}.

+ * + * @param expression a boolean expression + * @param msgSupplier the {@link Supplier#get()} set the + * exception message if valid. Otherwise, + * the message is {@link #CHECK_STATE_EX_MESSAGE} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState( + final boolean expression, + final Supplier msgSupplier) { + if (!expression) { + String msg; + try { + // note that we can get NPE evaluating the message itself; + // but we do not want this to override the actual NPE. + msg = msgSupplier.get(); + } catch (Exception e) { + LOG.debug("Error formatting message", e); + msg = CHECK_STATE_EX_MESSAGE; + } + throw new IllegalStateException(msg); + } + } + /* @VisibleForTesting */ static String getDefaultNullMSG() { return VALIDATE_IS_NOT_NULL_EX_MESSAGE; @@ -249,4 +336,9 @@ static String getDefaultNullMSG() { static String getDefaultCheckArgumentMSG() { return CHECK_ARGUMENT_EX_MESSAGE; } + + /* @VisibleForTesting */ + static String getDefaultCheckStateMSG() { + return CHECK_STATE_EX_MESSAGE; + } } diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPreconditions.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPreconditions.java index 47917662ad..4a11555535 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPreconditions.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPreconditions.java @@ -221,4 +221,104 @@ public void testCheckArgumentWithFailure() throws Exception { () -> Preconditions.checkArgument(false, () -> String.format(NULL_FORMATTER, NON_NULL_STRING))); } + + @Test + public void testCheckStateWithSuccess() throws Exception { + // success + Preconditions.checkState(true); + // null supplier + Preconditions.checkState(true, null); + // null message + Preconditions.checkState(true, (String) null); + Preconditions.checkState(true, NON_NULL_STRING); + // null in string format + Preconditions.checkState(true, EXPECTED_ERROR_MSG_ARGS, null, null); + // illegalformat + Preconditions.checkState(true, EXPECTED_ERROR_MSG_ARGS, 1, 2); + // ill-formated string supplier + Preconditions.checkState(true, ()-> String.format("%d", + NON_INT_STRING)); + // null pattern to string formatter + Preconditions.checkState(true, NULL_FORMATTER, null, 1); + // null arguments to string formatter + Preconditions.checkState(true, EXPECTED_ERROR_MSG_ARGS, + null, null); + // illegal format exception + Preconditions.checkState(true, "message %d %d", + NON_INT_STRING, 1); + // insufficient arguments + Preconditions.checkState(true, EXPECTED_ERROR_MSG_ARGS, + NON_INT_STRING); + // null format in string supplier + Preconditions.checkState(true, + () -> String.format(NULL_FORMATTER, NON_INT_STRING)); + } + + @Test + public void testCheckStateWithFailure() throws Exception { + // failure without message + LambdaTestUtils.intercept(IllegalStateException.class, + () -> Preconditions.checkState(false)); + errorMessage = null; + // failure with Null message + LambdaTestUtils.intercept(IllegalStateException.class, + null, + () -> Preconditions.checkState(false, errorMessage)); + // failure with message + errorMessage = EXPECTED_ERROR_MSG; + LambdaTestUtils.intercept(IllegalStateException.class, + errorMessage, + () -> Preconditions.checkState(false, errorMessage)); + + // failure with message format + errorMessage = EXPECTED_ERROR_MSG + " %s"; + String arg = "IllegalStaExcep"; + String expectedMSG = String.format(errorMessage, arg); + LambdaTestUtils.intercept(IllegalStateException.class, + expectedMSG, + () -> Preconditions.checkState(false, errorMessage, arg)); + + // failure with multiple arg message format + errorMessage = EXPECTED_ERROR_MSG_ARGS; + expectedMSG = + String.format(errorMessage, arg, 1); + LambdaTestUtils.intercept(IllegalStateException.class, + expectedMSG, + () -> Preconditions.checkState(false, errorMessage, arg, 1)); + + // ignore illegal format will be thrown if the case is not handled correctly + LambdaTestUtils.intercept(IllegalStateException.class, + Preconditions.getDefaultCheckStateMSG(), + () -> Preconditions.checkState(false, + errorMessage, 1, NON_INT_STRING)); + + // ignore illegal format will be thrown for insufficient Insufficient Args + LambdaTestUtils.intercept(IllegalStateException.class, + Preconditions.getDefaultCheckStateMSG(), + () -> Preconditions.checkState(false, errorMessage, NON_NULL_STRING)); + + // failure with Null supplier + final Supplier nullSupplier = null; + LambdaTestUtils.intercept(IllegalStateException.class, + null, + () -> Preconditions.checkState(false, nullSupplier)); + + // ignore illegal format in supplier + LambdaTestUtils.intercept(IllegalStateException.class, + Preconditions.getDefaultCheckStateMSG(), + () -> Preconditions.checkState(false, + () -> String.format(errorMessage, 1, NON_INT_STRING))); + + // ignore insufficient arguments in string Supplier + LambdaTestUtils.intercept(IllegalStateException.class, + Preconditions.getDefaultCheckStateMSG(), + () -> Preconditions.checkState(false, + () -> String.format(errorMessage, NON_NULL_STRING))); + + // ignore null formatter + LambdaTestUtils.intercept(IllegalStateException.class, + Preconditions.getDefaultCheckStateMSG(), + () -> Preconditions.checkState(false, + () -> String.format(NULL_FORMATTER, NON_NULL_STRING))); + } }