Skip to content

Commit 0d96ba5

Browse files
Improve error model and serialization
1 parent edb7e30 commit 0d96ba5

6 files changed

Lines changed: 664 additions & 14 deletions

File tree

nexus-sdk/src/main/java/io/nexusrpc/FailureInfo.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@ public static Builder newBuilder(FailureInfo failure) {
2020
}
2121

2222
private final String message;
23+
private final String stackTrace;
2324
private final Map<String, String> metadata;
2425
private final @Nullable String detailsJson;
2526

26-
private FailureInfo(String message, Map<String, String> metadata, @Nullable String detailsJson) {
27+
private FailureInfo(
28+
String message,
29+
String stackTrace,
30+
Map<String, String> metadata,
31+
@Nullable String detailsJson) {
2732
this.message = message;
33+
this.stackTrace = stackTrace;
2834
this.metadata = metadata;
2935
this.detailsJson = detailsJson;
3036
}
@@ -34,6 +40,11 @@ public String getMessage() {
3440
return message;
3541
}
3642

43+
/** Failure stack trace. */
44+
public String getStackTrace() {
45+
return stackTrace;
46+
}
47+
3748
/** Failure metadata. */
3849
public Map<String, String> getMetadata() {
3950
return metadata;
@@ -50,13 +61,14 @@ public boolean equals(Object o) {
5061
if (o == null || getClass() != o.getClass()) return false;
5162
FailureInfo that = (FailureInfo) o;
5263
return Objects.equals(message, that.message)
64+
&& Objects.equals(stackTrace, that.stackTrace)
5365
&& Objects.equals(metadata, that.metadata)
5466
&& Objects.equals(detailsJson, that.detailsJson);
5567
}
5668

5769
@Override
5870
public int hashCode() {
59-
return Objects.hash(message, metadata, detailsJson);
71+
return Objects.hash(message, stackTrace, metadata, detailsJson);
6072
}
6173

6274
@Override
@@ -65,6 +77,9 @@ public String toString() {
6577
+ "message='"
6678
+ message
6779
+ '\''
80+
+ ", stackTrace='"
81+
+ stackTrace
82+
+ '\''
6883
+ ", metadata="
6984
+ metadata
7085
+ ", details="
@@ -75,6 +90,7 @@ public String toString() {
7590
/** Builder for an operation failure. */
7691
public static class Builder {
7792
private @Nullable String message;
93+
private @Nullable String stackTrace;
7894
private final Map<String, String> metadata;
7995
private @Nullable String detailsJson;
8096

@@ -84,6 +100,7 @@ private Builder() {
84100

85101
private Builder(FailureInfo failure) {
86102
message = failure.message;
103+
stackTrace = failure.stackTrace;
87104
metadata = new HashMap<>(failure.metadata);
88105
detailsJson = failure.detailsJson;
89106
}
@@ -111,11 +128,17 @@ public Builder setDetailsJson(@Nullable String detailsJson) {
111128
return this;
112129
}
113130

131+
/** Set stack trace. */
132+
public Builder setStackTrace(@Nullable String stackTrace) {
133+
this.stackTrace = stackTrace;
134+
return this;
135+
}
136+
114137
/** Build the operation failure. */
115138
public FailureInfo build() {
116139
Objects.requireNonNull(message, "Message required");
117140
return new FailureInfo(
118-
message, Collections.unmodifiableMap(new HashMap<>(metadata)), detailsJson);
141+
message, stackTrace, Collections.unmodifiableMap(new HashMap<>(metadata)), detailsJson);
119142
}
120143
}
121144
}

nexus-sdk/src/main/java/io/nexusrpc/OperationException.java

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,76 @@
11
package io.nexusrpc;
22

3+
import org.jspecify.annotations.Nullable;
4+
35
/** An operation has failed or was canceled. */
46
public class OperationException extends Exception {
57
private final OperationState state;
68

7-
private OperationException(OperationState state, Throwable cause) {
8-
super(cause);
9+
private OperationException(OperationState state, String message, @Nullable Throwable cause) {
10+
super(message, cause);
911
this.state = state;
1012
}
1113

14+
/**
15+
* Create a failed operation exception with a message.
16+
*
17+
* @param message The failure message.
18+
* @return The operation exception.
19+
*/
20+
public static OperationException failure(String message) {
21+
return new OperationException(OperationState.FAILED, message, null);
22+
}
23+
24+
/**
25+
* Create a failed operation exception with a cause.
26+
*
27+
* @param cause The cause of the failure.
28+
* @return The operation exception.
29+
*/
1230
public static OperationException failure(Throwable cause) {
13-
return new OperationException(OperationState.FAILED, cause);
31+
return new OperationException(OperationState.FAILED, cause.toString(), cause);
32+
}
33+
34+
/**
35+
* Create a failed operation exception with a message and cause.
36+
*
37+
* @param message The failure message.
38+
* @param cause The cause of the failure.
39+
* @return The operation exception.
40+
*/
41+
public static OperationException failure(String message, Throwable cause) {
42+
return new OperationException(OperationState.FAILED, message, cause);
1443
}
1544

45+
/**
46+
* Create a canceled operation exception with a message.
47+
*
48+
* @param message The cancellation message.
49+
* @return The operation exception.
50+
*/
51+
public static OperationException canceled(String message) {
52+
return new OperationException(OperationState.CANCELED, message, null);
53+
}
54+
55+
/**
56+
* Create a canceled operation exception with a cause.
57+
*
58+
* @param cause The cause of the cancellation.
59+
* @return The operation exception.
60+
*/
1661
public static OperationException canceled(Throwable cause) {
17-
return new OperationException(OperationState.CANCELED, cause);
62+
return new OperationException(OperationState.CANCELED, cause.toString(), cause);
63+
}
64+
65+
/**
66+
* Create a canceled operation exception with a message and cause.
67+
*
68+
* @param message The cancellation message.
69+
* @param cause The cause of the cancellation.
70+
* @return The operation exception.
71+
*/
72+
public static OperationException canceled(String message, Throwable cause) {
73+
return new OperationException(OperationState.CANCELED, message, cause);
1874
}
1975

2076
public OperationState getState() {

nexus-sdk/src/main/java/io/nexusrpc/handler/HandlerException.java

Lines changed: 138 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.nexusrpc.handler;
22

3+
import io.nexusrpc.FailureInfo;
34
import java.util.Arrays;
45
import org.jspecify.annotations.Nullable;
56

@@ -33,36 +34,160 @@ public enum RetryBehavior {
3334
private final String rawErrorType;
3435
private final ErrorType errorType;
3536
private final RetryBehavior retryBehavior;
37+
private final FailureInfo originalFailure;
3638

37-
public HandlerException(ErrorType errorType, String message) {
38-
this(errorType, new RuntimeException(message), RetryBehavior.UNSPECIFIED);
39+
/**
40+
* Create a handler exception with the given error type and cause message.
41+
*
42+
* @param errorType The error type.
43+
* @param causeMessage The cause message.
44+
* @deprecated
45+
*/
46+
public HandlerException(ErrorType errorType, String causeMessage) {
47+
this(errorType, new RuntimeException(causeMessage), RetryBehavior.UNSPECIFIED);
48+
}
49+
50+
/**
51+
* Create a handler exception with the given error type and cause message.
52+
*
53+
* @param errorType The error type.
54+
* @param causeMessage The cause message.
55+
* @param retryBehavior The retry behavior for this exception.
56+
* @deprecated
57+
*/
58+
public HandlerException(ErrorType errorType, String causeMessage, RetryBehavior retryBehavior) {
59+
this(errorType, new RuntimeException(causeMessage), retryBehavior);
3960
}
4061

41-
public HandlerException(ErrorType errorType, String message, RetryBehavior retryBehavior) {
42-
this(errorType, new RuntimeException(message), retryBehavior);
62+
/**
63+
* Create a handler exception with the given error type, message, and cause.
64+
*
65+
* @param errorType The error type.
66+
* @param message The error message.
67+
* @param cause The cause of this exception.
68+
*/
69+
public HandlerException(ErrorType errorType, String message, @Nullable Throwable cause) {
70+
this(errorType, message, cause, RetryBehavior.UNSPECIFIED);
4371
}
4472

73+
/**
74+
* Create a handler exception with the given error type and cause.
75+
*
76+
* @param errorType The error type.
77+
* @param cause The cause of this exception.
78+
*/
4579
public HandlerException(ErrorType errorType, @Nullable Throwable cause) {
4680
this(errorType, cause, RetryBehavior.UNSPECIFIED);
4781
}
4882

83+
/**
84+
* Create a handler exception with the given error type, cause, and retry behavior.
85+
*
86+
* @param errorType The error type.
87+
* @param cause The cause of this exception.
88+
* @param retryBehavior The retry behavior for this exception.
89+
*/
4990
public HandlerException(
5091
ErrorType errorType, @Nullable Throwable cause, RetryBehavior retryBehavior) {
51-
super(cause == null ? "handler error" : "handler error: " + cause.getMessage(), cause);
92+
this(
93+
errorType,
94+
cause == null ? "handler error" : "handler error: " + cause.getMessage(),
95+
cause,
96+
retryBehavior);
97+
}
98+
99+
/**
100+
* Create a handler exception with the given error type, message, cause, and retry behavior.
101+
*
102+
* @param errorType The error type.
103+
* @param message The error message.
104+
* @param cause The cause of this exception.
105+
* @param retryBehavior The retry behavior for this exception.
106+
*/
107+
public HandlerException(
108+
ErrorType errorType, String message, @Nullable Throwable cause, RetryBehavior retryBehavior) {
109+
this(errorType, message, cause, retryBehavior, null);
110+
}
111+
112+
/**
113+
* Create a handler exception with the given error type, message, cause, retry behavior, and
114+
* original failure.
115+
*
116+
* @param errorType The error type.
117+
* @param message The error message.
118+
* @param cause The cause of this exception.
119+
* @param retryBehavior The retry behavior for this exception.
120+
* @param originalFailure The original failure information if available.
121+
*/
122+
public HandlerException(
123+
ErrorType errorType,
124+
String message,
125+
@Nullable Throwable cause,
126+
RetryBehavior retryBehavior,
127+
FailureInfo originalFailure) {
128+
super(message, cause);
52129
this.rawErrorType = errorType.name();
53-
this.errorType = errorType;
130+
this.errorType =
131+
Arrays.stream(ErrorType.values()).anyMatch((t) -> t.name().equals(rawErrorType))
132+
? ErrorType.valueOf(rawErrorType)
133+
: ErrorType.UNKNOWN;
54134
this.retryBehavior = retryBehavior;
135+
this.originalFailure = originalFailure;
55136
}
56137

138+
/**
139+
* Create a handler exception with a raw error type string, cause, and retry behavior.
140+
*
141+
* @param rawErrorType The raw error type string.
142+
* @param cause The cause of this exception.
143+
* @param retryBehavior The retry behavior for this exception.
144+
*/
57145
public HandlerException(
58146
String rawErrorType, @Nullable Throwable cause, RetryBehavior retryBehavior) {
59-
super(cause == null ? "handler error" : "handler error: " + cause.getMessage(), cause);
147+
this(
148+
rawErrorType,
149+
cause == null ? "handler error" : "handler error: " + cause.getMessage(),
150+
cause,
151+
retryBehavior);
152+
}
153+
154+
/**
155+
* Create a handler exception with a raw error type string, message, cause, and retry behavior.
156+
*
157+
* @param rawErrorType The raw error type string.
158+
* @param message The error message.
159+
* @param cause The cause of this exception.
160+
* @param retryBehavior The retry behavior for this exception.
161+
*/
162+
public HandlerException(
163+
String rawErrorType, String message, @Nullable Throwable cause, RetryBehavior retryBehavior) {
164+
this(rawErrorType, message, cause, retryBehavior, null);
165+
}
166+
167+
/**
168+
* Create a handler exception with a raw error type string, message, cause, retry behavior, and
169+
* original failure.
170+
*
171+
* @param rawErrorType The raw error type string.
172+
* @param message The error message.
173+
* @param cause The cause of this exception.
174+
* @param retryBehavior The retry behavior for this exception.
175+
* @param originalFailure The original failure information if available.
176+
*/
177+
public HandlerException(
178+
String rawErrorType,
179+
String message,
180+
@Nullable Throwable cause,
181+
RetryBehavior retryBehavior,
182+
@Nullable FailureInfo originalFailure) {
183+
super(message, cause);
60184
this.rawErrorType = rawErrorType;
61185
this.errorType =
62186
Arrays.stream(ErrorType.values()).anyMatch((t) -> t.name().equals(rawErrorType))
63187
? ErrorType.valueOf(rawErrorType)
64188
: ErrorType.UNKNOWN;
65189
this.retryBehavior = retryBehavior;
190+
this.originalFailure = originalFailure;
66191
}
67192

68193
/**
@@ -86,6 +211,12 @@ public RetryBehavior getRetryBehavior() {
86211
return retryBehavior;
87212
}
88213

214+
/** Original FailureInfo if available */
215+
@Nullable
216+
public FailureInfo getOriginalFailure() {
217+
return originalFailure;
218+
}
219+
89220
public boolean isRetryable() {
90221
if (retryBehavior != RetryBehavior.UNSPECIFIED) {
91222
return retryBehavior == RetryBehavior.RETRYABLE;

0 commit comments

Comments
 (0)