Skip to content

Commit 0f2602a

Browse files
[fit] Retrieve the first non-null exception message from the exception chain. (#176)
1 parent a5488d7 commit 0f2602a

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

framework/fit/java/fit-util/src/main/java/modelengine/fitframework/util/ExceptionUtils.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public static Throwable getActualCause(MethodInvocationException exception) {
5555
/**
5656
* 获取异常的原因。
5757
*
58-
* @param throwable 表示指定异常的 {@link Exception}。
58+
* @param throwable 表示指定异常的 {@link Throwable}。
5959
* @return 表示异常原因的 {@link String}。
6060
*/
6161
public static String getReason(Throwable throwable) {
@@ -64,4 +64,23 @@ public static String getReason(Throwable throwable) {
6464
}
6565
return throwable.getClass().getSimpleName() + ": " + throwable.getMessage();
6666
}
67+
68+
/**
69+
* 获取从异常链中找出第一个非空的异常消息。
70+
*
71+
* @param throwable 表示指定异常的 {@link Throwable}。
72+
* @return 表示异常原因的 {@link String}。
73+
*/
74+
public static String getActualMessage(Throwable throwable) {
75+
Set<Throwable> visited = new HashSet<>();
76+
while (throwable != null && !visited.contains(throwable)) {
77+
visited.add(throwable);
78+
String message = throwable.getMessage();
79+
if (StringUtils.isNotBlank(message)) {
80+
return message;
81+
}
82+
throwable = throwable.getCause();
83+
}
84+
return null;
85+
}
6786
}

framework/fit/java/fit-util/src/test/java/modelengine/fitframework/util/ExceptionUtilsTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,43 @@ void givenExceptionCauseHasAnotherTypeCauseThenGetItsDescendantsCause() {
106106
assertThat(actualCause).isNotNull().hasMessage("error");
107107
}
108108
}
109+
110+
@Nested
111+
@DisplayName("test getActualMessage(Throwable throwable)")
112+
class WhenGetActualMessage {
113+
@Test
114+
@DisplayName("when throwable has non-blank message, return the message")
115+
void givenThrowableWithMessageThenReturnMessage() {
116+
Throwable throwable = new Exception("Error occurred");
117+
String message = ExceptionUtils.getActualMessage(throwable);
118+
assertThat(message).isEqualTo("Error occurred");
119+
}
120+
121+
@Test
122+
@DisplayName("when throwable chain has non-blank message in deep cause, return the first valid message")
123+
void givenDeepThrowableChainThenReturnFirstValidMessage() {
124+
Throwable cause = new Exception("Root cause");
125+
Throwable throwable = new Exception(null, new Exception(" ", cause));
126+
String message = ExceptionUtils.getActualMessage(throwable);
127+
assertThat(message).isEqualTo("Root cause");
128+
}
129+
130+
@Test
131+
@DisplayName("when throwable chain has all blank messages, return null")
132+
void givenAllBlankMessagesThenReturnNull() {
133+
Throwable throwable = new Exception(null, new Exception(" "));
134+
String message = ExceptionUtils.getActualMessage(throwable);
135+
assertThat(message).isNull();
136+
}
137+
138+
@Test
139+
@DisplayName("when throwable chain has circular reference, return valid message")
140+
void givenCircularReferenceThenReturnFirstValidMessage() {
141+
Exception e1 = new Exception("e1");
142+
Exception e2 = new Exception("e2", e1);
143+
e1.initCause(e2);
144+
assertThat(ExceptionUtils.getActualMessage(e1)).isEqualTo("e1");
145+
assertThat(ExceptionUtils.getActualMessage(e2)).isEqualTo("e2");
146+
}
147+
}
109148
}

0 commit comments

Comments
 (0)