[ISSUE-3007][Improve] Introduce a utils to process conditions check and exceptions thrown for console-service module. (#3652)
Co-authored-by: zhengkezhou <36791902+zzzk1@users.noreply.github.com>
diff --git a/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/base/util/Throws.java b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/base/util/Throws.java
new file mode 100644
index 0000000..8fce0e7
--- /dev/null
+++ b/streampark-console/streampark-console-service/src/main/java/org/apache/streampark/console/base/util/Throws.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.streampark.console.base.util;
+
+import org.apache.streampark.common.util.AssertUtils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+
+import javax.annotation.Nonnull;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.TimeUnit;
+
+import scala.Tuple2;
+
+/** The util to throw a sub-exception of {@link RuntimeException} for the specified condition. */
+public class Throws {
+
+ private static final Cache<Tuple2<Class<?>, Class<?>>, Constructor<?>> CACHE =
+ Caffeine.newBuilder().expireAfterAccess(1, TimeUnit.DAYS).maximumSize(32).build();
+
+ private Throws() {}
+
+ /**
+ * Throw a runtime exception to the specified class type and condition.
+ *
+ * @param bool The condition.
+ * @param exceptionClass The target runtime exception class type.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfTrue(
+ boolean bool, @Nonnull Class<T> exceptionClass) {
+ if (bool) {
+ throw createRuntimeException(exceptionClass);
+ }
+ }
+
+ /**
+ * Throw a runtime exception to the specified class type and condition.
+ *
+ * @param bool The condition.
+ * @param exceptionClass The target runtime exception class type.
+ * @param msgFormat The error message or message format.
+ * @param args The real value of placeholders.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfTrue(
+ boolean bool, @Nonnull Class<T> exceptionClass, String msgFormat, Object... args) {
+ if (bool) {
+ throw createRuntimeException(exceptionClass, getErrorMsg(msgFormat, args));
+ }
+ }
+
+ /**
+ * Throw a runtime exception to the specified class type and condition.
+ *
+ * @param bool The condition.
+ * @param exceptionClass The target runtime exception class type.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfFalse(
+ boolean bool, @Nonnull Class<T> exceptionClass) {
+ throwIfTrue(!bool, exceptionClass);
+ }
+
+ /**
+ * Throw a runtime exception to the specified class type and condition.
+ *
+ * @param bool The condition.
+ * @param exceptionClass The target runtime exception class type.
+ * @param msgFormat The error message or message format.
+ * @param args The real value of placeholders.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfFalse(
+ boolean bool, @Nonnull Class<T> exceptionClass, String msgFormat, Object... args) {
+ throwIfTrue(!bool, exceptionClass, msgFormat, args);
+ }
+
+ /**
+ * Throw a runtime exception to the specified class type and object.
+ *
+ * @param obj The object.
+ * @param exceptionClass The target runtime exception class type.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfNull(
+ Object obj, @Nonnull Class<T> exceptionClass) {
+ throwIfTrue(obj == null, exceptionClass);
+ }
+
+ /**
+ * Throw a runtime exception to the specified class type and object.
+ *
+ * @param obj The object.
+ * @param exceptionClass The target runtime exception class type.
+ * @param msgFormat The error message or message format.
+ * @param args The real value of placeholders.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfNull(
+ Object obj, @Nonnull Class<T> exceptionClass, String msgFormat, Object... args) {
+ throwIfTrue(obj == null, exceptionClass, msgFormat, args);
+ }
+
+ /**
+ * Throw a runtime exception to the specified class type and object.
+ *
+ * @param obj The object.
+ * @param exceptionClass The target runtime exception class type.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfNonnull(
+ Object obj, @Nonnull Class<T> exceptionClass) {
+ throwIfTrue(obj != null, exceptionClass);
+ }
+
+ /**
+ * Throw a runtime exception to the specified class type and object.
+ *
+ * @param obj The object.
+ * @param exceptionClass The target runtime exception class type.
+ * @param msgFormat The error message or message format.
+ * @param args The real value of placeholders.
+ * @param <T> The target exception class.
+ */
+ public static <T extends RuntimeException> void throwIfNonnull(
+ Object obj, @Nonnull Class<T> exceptionClass, String msgFormat, Object... args) {
+ throwIfTrue(obj != null, exceptionClass, msgFormat, args);
+ }
+
+ private static <T extends RuntimeException> T createRuntimeException(
+ @Nonnull Class<T> exceptionClass, String... errorMsgs) {
+ try {
+ return createRuntimeExceptionInternal(exceptionClass, errorMsgs);
+ } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static <T extends RuntimeException> T createRuntimeExceptionInternal(
+ @Nonnull Class<T> exceptionClass, String... errorMsgs)
+ throws InvocationTargetException, InstantiationException, IllegalAccessException {
+ AssertUtils.notNull(exceptionClass, "The target exception must be specified.");
+ Tuple2<Class<?>, Class<?>> key =
+ new Tuple2<>(exceptionClass, hasElements(errorMsgs) ? String.class : null);
+ Constructor<?> constructor =
+ CACHE.get(
+ key,
+ classAndParams -> {
+ try {
+ return hasElements(errorMsgs)
+ ? exceptionClass.getConstructor(String.class)
+ : exceptionClass.getConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ AssertUtils.notNull(
+ constructor,
+ String.format("There's no a constructor for exception '%s'.", exceptionClass.getName()));
+ return hasElements(errorMsgs)
+ ? (T) constructor.newInstance(errorMsgs[0])
+ : (T) constructor.newInstance();
+ }
+
+ private static boolean hasElements(Object[] objects) {
+ return objects != null && objects.length > 0;
+ }
+
+ private static String getErrorMsg(String msgFormat, Object... args) {
+ if (StringUtils.isBlank(msgFormat)) {
+ return msgFormat;
+ }
+ if (args == null || args.length < 1) {
+ return msgFormat;
+ }
+ return String.format(msgFormat, args);
+ }
+}
diff --git a/streampark-console/streampark-console-service/src/test/java/org/apache/streampark/console/core/utils/ThrowsTest.java b/streampark-console/streampark-console-service/src/test/java/org/apache/streampark/console/core/utils/ThrowsTest.java
new file mode 100644
index 0000000..ae2201a
--- /dev/null
+++ b/streampark-console/streampark-console-service/src/test/java/org/apache/streampark/console/core/utils/ThrowsTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.streampark.console.core.utils;
+
+import org.apache.streampark.console.base.exception.AlertException;
+import org.apache.streampark.console.base.util.Throws;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class ThrowsTest {
+
+ static final String PURE_MSG_TPL = "Pure test string.";
+ static final String MSG_TPL = "Template string %s.";
+ static final String ARG = "Hello";
+ static final String RENDERED_STR = String.format(MSG_TPL, ARG);
+
+ public static class TestRuntimeException extends RuntimeException {
+ public TestRuntimeException() {}
+ }
+
+ @Test
+ void testThrowIfTrue() {
+ assertThatThrownBy(() -> Throws.throwIfTrue(true, null))
+ .hasMessage("The target exception must be specified.");
+ assertThatThrownBy(() -> Throws.throwIfTrue(true, AlertException.class))
+ .hasCauseInstanceOf(NoSuchMethodException.class);
+ assertThatThrownBy(() -> Throws.throwIfTrue(true, AlertException.class, PURE_MSG_TPL))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(PURE_MSG_TPL);
+ assertThatThrownBy(() -> Throws.throwIfTrue(true, AlertException.class, MSG_TPL, ARG))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(RENDERED_STR);
+ assertThatThrownBy(() -> Throws.throwIfTrue(true, TestRuntimeException.class))
+ .isInstanceOf(TestRuntimeException.class);
+
+ Throws.throwIfTrue(false, null);
+ Throws.throwIfTrue(false, AlertException.class);
+ Throws.throwIfTrue(false, AlertException.class, PURE_MSG_TPL);
+ Throws.throwIfTrue(false, AlertException.class, MSG_TPL, ARG);
+ Throws.throwIfTrue(false, TestRuntimeException.class);
+ }
+
+ @Test
+ void testThrowIfFalse() {
+ assertThatThrownBy(() -> Throws.throwIfFalse(false, null))
+ .hasMessage("The target exception must be specified.");
+ assertThatThrownBy(() -> Throws.throwIfFalse(false, AlertException.class))
+ .hasCauseInstanceOf(NoSuchMethodException.class);
+ assertThatThrownBy(() -> Throws.throwIfFalse(false, AlertException.class, PURE_MSG_TPL))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(PURE_MSG_TPL);
+ assertThatThrownBy(() -> Throws.throwIfFalse(false, AlertException.class, MSG_TPL, ARG))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(RENDERED_STR);
+ assertThatThrownBy(() -> Throws.throwIfFalse(false, TestRuntimeException.class))
+ .isInstanceOf(TestRuntimeException.class);
+
+ Throws.throwIfFalse(true, null);
+ Throws.throwIfFalse(true, AlertException.class);
+ Throws.throwIfFalse(true, AlertException.class, PURE_MSG_TPL);
+ Throws.throwIfFalse(true, AlertException.class, MSG_TPL, ARG);
+ Throws.throwIfFalse(true, TestRuntimeException.class);
+ }
+
+ @Test
+ void testThrowIfNull() {
+ assertThatThrownBy(() -> Throws.throwIfNull(null, null))
+ .hasMessage("The target exception must be specified.");
+ assertThatThrownBy(() -> Throws.throwIfNull(null, AlertException.class))
+ .hasCauseInstanceOf(NoSuchMethodException.class);
+ assertThatThrownBy(() -> Throws.throwIfNull(null, AlertException.class, PURE_MSG_TPL))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(PURE_MSG_TPL);
+ assertThatThrownBy(() -> Throws.throwIfNull(null, AlertException.class, MSG_TPL, ARG))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(RENDERED_STR);
+ assertThatThrownBy(() -> Throws.throwIfNull(null, TestRuntimeException.class))
+ .isInstanceOf(TestRuntimeException.class);
+
+ Throws.throwIfNull(this, null);
+ Throws.throwIfNull(this, AlertException.class);
+ Throws.throwIfNull(this, AlertException.class, PURE_MSG_TPL);
+ Throws.throwIfNull(this, AlertException.class, MSG_TPL, ARG);
+ Throws.throwIfNull(this, TestRuntimeException.class);
+ }
+
+ @Test
+ void testThrowIfNonnull() {
+ assertThatThrownBy(() -> Throws.throwIfNonnull(this, null))
+ .hasMessage("The target exception must be specified.");
+ assertThatThrownBy(() -> Throws.throwIfNonnull(this, AlertException.class))
+ .hasCauseInstanceOf(NoSuchMethodException.class);
+ assertThatThrownBy(() -> Throws.throwIfNonnull(this, AlertException.class, PURE_MSG_TPL))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(PURE_MSG_TPL);
+ assertThatThrownBy(() -> Throws.throwIfNonnull(this, AlertException.class, MSG_TPL, ARG))
+ .isInstanceOf(AlertException.class)
+ .hasMessage(RENDERED_STR);
+ assertThatThrownBy(() -> Throws.throwIfNonnull(this, TestRuntimeException.class))
+ .isInstanceOf(TestRuntimeException.class);
+
+ Throws.throwIfNonnull(null, null);
+ Throws.throwIfNonnull(null, AlertException.class);
+ Throws.throwIfNonnull(null, AlertException.class, PURE_MSG_TPL);
+ Throws.throwIfNonnull(null, AlertException.class, MSG_TPL, ARG);
+ Throws.throwIfNonnull(null, TestRuntimeException.class);
+ }
+}