Skip to content

Commit e6e9dea

Browse files
Improve error model and serialization (#44)
Improve error model and serialization
1 parent c07274b commit e6e9dea

6 files changed

Lines changed: 682 additions & 12 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 @Nullable 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+
@Nullable 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 @Nullable 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: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,96 @@
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) {
9+
private OperationException(OperationState state, String message, @Nullable Throwable cause) {
10+
super(message, cause);
11+
this.state = state;
12+
}
13+
14+
private OperationException(OperationState state, @Nullable Throwable cause) {
815
super(cause);
916
this.state = state;
1017
}
1118

12-
public static OperationException failure(Throwable cause) {
19+
/**
20+
* Create a failed operation exception with a message.
21+
*
22+
* @param message The failure message.
23+
* @return The operation exception.
24+
*/
25+
public static OperationException failed(String message) {
26+
return new OperationException(OperationState.FAILED, message, null);
27+
}
28+
29+
/**
30+
* Create a failed operation exception with a cause.
31+
*
32+
* @param cause The cause of the failure.
33+
* @return The operation exception.
34+
*/
35+
public static OperationException failed(Throwable cause) {
1336
return new OperationException(OperationState.FAILED, cause);
1437
}
1538

39+
/**
40+
* Create a failed operation exception with a message and cause.
41+
*
42+
* @param message The failure message.
43+
* @param cause The cause of the failure.
44+
* @return The operation exception.
45+
*/
46+
public static OperationException failed(String message, Throwable cause) {
47+
return new OperationException(OperationState.FAILED, message, cause);
48+
}
49+
50+
/**
51+
* Create a failed operation exception with a cause.
52+
*
53+
* @param cause The cause of the failure.
54+
* @return The operation exception.
55+
* @deprecated Use {@link #failed(Throwable)} instead. This factory method will be removed in a
56+
* future release.
57+
*/
58+
@Deprecated
59+
public static OperationException failure(Throwable cause) {
60+
return failed(cause);
61+
}
62+
63+
/**
64+
* Create a canceled operation exception with a message.
65+
*
66+
* @param message The cancellation message.
67+
* @return The operation exception.
68+
*/
69+
public static OperationException canceled(String message) {
70+
return new OperationException(OperationState.CANCELED, message, null);
71+
}
72+
73+
/**
74+
* Create a canceled operation exception with a cause.
75+
*
76+
* @param cause The cause of the cancellation.
77+
* @return The operation exception.
78+
*/
1679
public static OperationException canceled(Throwable cause) {
1780
return new OperationException(OperationState.CANCELED, cause);
1881
}
1982

83+
/**
84+
* Create a canceled operation exception with a message and cause.
85+
*
86+
* @param message The cancellation message.
87+
* @param cause The cause of the cancellation.
88+
* @return The operation exception.
89+
*/
90+
public static OperationException canceled(String message, Throwable cause) {
91+
return new OperationException(OperationState.CANCELED, message, cause);
92+
}
93+
2094
public OperationState getState() {
2195
return state;
2296
}

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

Lines changed: 140 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,162 @@ 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 Use {@link #HandlerException(ErrorType, String, Throwable)} instead. This
45+
* constructor will be removed in a future release.
46+
*/
47+
public HandlerException(ErrorType errorType, String causeMessage) {
48+
this(errorType, new RuntimeException(causeMessage), RetryBehavior.UNSPECIFIED);
49+
}
50+
51+
/**
52+
* Create a handler exception with the given error type and cause message.
53+
*
54+
* @param errorType The error type.
55+
* @param causeMessage The cause message.
56+
* @param retryBehavior The retry behavior for this exception.
57+
* @deprecated Use {@link #HandlerException(ErrorType, String, Throwable, RetryBehavior)} instead.
58+
* This constructor will be removed in a future release.
59+
*/
60+
public HandlerException(ErrorType errorType, String causeMessage, RetryBehavior retryBehavior) {
61+
this(errorType, new RuntimeException(causeMessage), retryBehavior);
3962
}
4063

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

75+
/**
76+
* Create a handler exception with the given error type and cause.
77+
*
78+
* @param errorType The error type.
79+
* @param cause The cause of this exception.
80+
*/
4581
public HandlerException(ErrorType errorType, @Nullable Throwable cause) {
4682
this(errorType, cause, RetryBehavior.UNSPECIFIED);
4783
}
4884

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

140+
/**
141+
* Create a handler exception with a raw error type string, cause, and retry behavior.
142+
*
143+
* @param rawErrorType The raw error type string.
144+
* @param cause The cause of this exception.
145+
* @param retryBehavior The retry behavior for this exception.
146+
*/
57147
public HandlerException(
58148
String rawErrorType, @Nullable Throwable cause, RetryBehavior retryBehavior) {
59-
super(cause == null ? "handler error" : "handler error: " + cause.getMessage(), cause);
149+
this(
150+
rawErrorType,
151+
cause == null ? "handler error" : "handler error: " + cause.getMessage(),
152+
cause,
153+
retryBehavior);
154+
}
155+
156+
/**
157+
* Create a handler exception with a raw error type string, message, cause, and retry behavior.
158+
*
159+
* @param rawErrorType The raw error type string.
160+
* @param message The error message.
161+
* @param cause The cause of this exception.
162+
* @param retryBehavior The retry behavior for this exception.
163+
*/
164+
public HandlerException(
165+
String rawErrorType, String message, @Nullable Throwable cause, RetryBehavior retryBehavior) {
166+
this(rawErrorType, message, cause, retryBehavior, null);
167+
}
168+
169+
/**
170+
* Create a handler exception with a raw error type string, message, cause, retry behavior, and
171+
* original failure.
172+
*
173+
* @param rawErrorType The raw error type string.
174+
* @param message The error message.
175+
* @param cause The cause of this exception.
176+
* @param retryBehavior The retry behavior for this exception.
177+
* @param originalFailure The original failure information if available.
178+
*/
179+
public HandlerException(
180+
String rawErrorType,
181+
String message,
182+
@Nullable Throwable cause,
183+
RetryBehavior retryBehavior,
184+
@Nullable FailureInfo originalFailure) {
185+
super(message, cause);
60186
this.rawErrorType = rawErrorType;
61187
this.errorType =
62188
Arrays.stream(ErrorType.values()).anyMatch((t) -> t.name().equals(rawErrorType))
63189
? ErrorType.valueOf(rawErrorType)
64190
: ErrorType.UNKNOWN;
65191
this.retryBehavior = retryBehavior;
192+
this.originalFailure = originalFailure;
66193
}
67194

68195
/**
@@ -86,6 +213,12 @@ public RetryBehavior getRetryBehavior() {
86213
return retryBehavior;
87214
}
88215

216+
/** Original FailureInfo if available */
217+
@Nullable
218+
public FailureInfo getOriginalFailure() {
219+
return originalFailure;
220+
}
221+
89222
public boolean isRetryable() {
90223
if (retryBehavior != RetryBehavior.UNSPECIFIED) {
91224
return retryBehavior == RetryBehavior.RETRYABLE;

0 commit comments

Comments
 (0)