Skip to content

Commit 851af64

Browse files
Add ApplicationFailure.Builder
1 parent 3c9e819 commit 851af64

5 files changed

Lines changed: 165 additions & 71 deletions

File tree

temporal-sdk/src/main/java/io/temporal/failure/ApplicationErrorCategory.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,4 @@ public enum ApplicationErrorCategory {
2929
UNSPECIFIED,
3030
/** Expected application error with little/no severity. */
3131
BENIGN,
32-
;
3332
}

temporal-sdk/src/main/java/io/temporal/failure/ApplicationFailure.java

Lines changed: 131 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
* <li>nonRetryable is set to false
5252
* <li>details are set to null
5353
* <li>stack trace is copied from the original exception
54-
* <li>category is set to ApplicationErrorCategory.APPLICATION_ERROR_CATEGORY_UNSPECIFIED
54+
* <li>category is set to {@link ApplicationErrorCategory#UNSPECIFIED}
5555
* </ul>
5656
*/
5757
public final class ApplicationFailure extends TemporalFailure {
@@ -61,6 +61,16 @@ public final class ApplicationFailure extends TemporalFailure {
6161
private Duration nextRetryDelay;
6262
private final ApplicationErrorCategory category;
6363

64+
/** Creates a new builder for {@link ApplicationFailure}. */
65+
public static ApplicationFailure.Builder newBuilder() {
66+
return new ApplicationFailure.Builder();
67+
}
68+
69+
/** Creates a new builder for {@link ApplicationFailure} initialized with the provided failure. */
70+
public static ApplicationFailure.Builder newBuilder(ApplicationFailure options) {
71+
return new ApplicationFailure.Builder(options);
72+
}
73+
6474
/**
6575
* New ApplicationFailure with {@link #isNonRetryable()} flag set to false.
6676
*
@@ -178,45 +188,7 @@ public static ApplicationFailure newNonRetryableFailureWithCause(
178188
ApplicationErrorCategory.UNSPECIFIED);
179189
}
180190

181-
/**
182-
* New ApplicationFailure with a specified category and {@link #isNonRetryable()} flag set to
183-
* false.
184-
*
185-
* <p>Note that this exception still may not be retried by the service if its type is included in
186-
* the doNotRetry property of the correspondent retry policy.
187-
*
188-
* @param message optional error message
189-
* @param type error type
190-
* @param category the category of the application failure.
191-
* @param cause failure cause. Each element of the cause chain will be converted to
192-
* ApplicationFailure for network transmission across network if it doesn't extend {@link
193-
* TemporalFailure}
194-
* @param details optional details about the failure. They are serialized using the same approach
195-
* as arguments and results.
196-
*/
197-
public static ApplicationFailure newFailureWithCategory(
198-
String message,
199-
String type,
200-
ApplicationErrorCategory category,
201-
@Nullable Throwable cause,
202-
Object... details) {
203-
return new ApplicationFailure(
204-
message, type, false, new EncodedValues(details), cause, null, category);
205-
}
206-
207-
static ApplicationFailure newFromValues(
208-
String message,
209-
String type,
210-
boolean nonRetryable,
211-
Values details,
212-
Throwable cause,
213-
Duration nextRetryDelay,
214-
ApplicationErrorCategory category) {
215-
return new ApplicationFailure(
216-
message, type, nonRetryable, details, cause, nextRetryDelay, category);
217-
}
218-
219-
ApplicationFailure(
191+
private ApplicationFailure(
220192
String message,
221193
String type,
222194
boolean nonRetryable,
@@ -274,4 +246,123 @@ private static String getMessage(String message, String type, boolean nonRetryab
274246
+ ", nonRetryable="
275247
+ nonRetryable;
276248
}
249+
250+
public static final class Builder {
251+
private String message;
252+
private String type;
253+
private Values details;
254+
private boolean nonRetryable;
255+
private Throwable cause;
256+
private Duration nextRetryDelay;
257+
private ApplicationErrorCategory category;
258+
259+
private Builder() {}
260+
261+
private Builder(ApplicationFailure options) {
262+
if (options == null) {
263+
return;
264+
}
265+
this.message = options.getOriginalMessage();
266+
this.type = options.type;
267+
this.details = options.details;
268+
this.nonRetryable = options.nonRetryable;
269+
this.nextRetryDelay = options.nextRetryDelay;
270+
this.category = options.category;
271+
}
272+
273+
/**
274+
* Sets the error type of this failure. This is used by {@link
275+
* io.temporal.common.RetryOptions.Builder#setDoNotRetry(String...)} to determine if the
276+
* exception is non retryable.
277+
*/
278+
public Builder setType(String type) {
279+
this.type = type;
280+
return this;
281+
}
282+
283+
284+
/**
285+
* Set the optional error message.
286+
*
287+
* <p>Default is "".
288+
*/
289+
public Builder setMessage(String message) {
290+
this.message = message;
291+
return this;
292+
}
293+
294+
/**
295+
* Set the optional details of the failure.
296+
*
297+
* <p>Details are serialized using the same approach as arguments and results.
298+
*/
299+
public Builder setDetails(Object... details) {
300+
this.details = new EncodedValues(details);
301+
return this;
302+
}
303+
304+
/**
305+
* Set the optional details of the failure.
306+
*
307+
* <p>Details are serialized using the same approach as arguments and results.
308+
*/
309+
public Builder setDetails(Values details) {
310+
this.details = details;
311+
return this;
312+
}
313+
314+
/**
315+
* Set the non retryable flag on the failure.
316+
*
317+
* <p>It means that this exception is not going to be retried even if it is not included into
318+
* retry policy doNotRetry list.
319+
*
320+
* <p>Default is false.
321+
*/
322+
public Builder setNonRetryable(boolean nonRetryable) {
323+
this.nonRetryable = nonRetryable;
324+
return this;
325+
}
326+
327+
/**
328+
* Set the optional cause of the failure. Each element of the cause chain will be converted to
329+
* {@link ApplicationFailure} for network transmission across network if it doesn't extend
330+
* {@link TemporalFailure}.
331+
*/
332+
public Builder setCause(Throwable cause) {
333+
this.cause = cause;
334+
return this;
335+
}
336+
337+
/**
338+
* Set the optional delay before the next retry attempt. Overrides the normal retry delay.
339+
*
340+
* <p>Default is null.
341+
*/
342+
public Builder setNextRetryDelay(Duration nextRetryDelay) {
343+
this.nextRetryDelay = nextRetryDelay;
344+
return this;
345+
}
346+
347+
/**
348+
* Set the optional category of the failure.
349+
*
350+
* <p>Default is {@link ApplicationErrorCategory#UNSPECIFIED}.
351+
*/
352+
public Builder setCategory(ApplicationErrorCategory category) {
353+
this.category = category;
354+
return this;
355+
}
356+
357+
public ApplicationFailure build() {
358+
return new ApplicationFailure(
359+
message,
360+
type,
361+
nonRetryable,
362+
details == null ? new EncodedValues(null) : details,
363+
cause,
364+
nextRetryDelay,
365+
category);
366+
}
367+
}
277368
}

temporal-sdk/src/main/java/io/temporal/failure/DefaultFailureConverter.java

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,18 @@ private RuntimeException failureToExceptionImpl(Failure failure, DataConverter d
9999
ApplicationFailureInfo info = failure.getApplicationFailureInfo();
100100
Optional<Payloads> details =
101101
info.hasDetails() ? Optional.of(info.getDetails()) : Optional.empty();
102-
return ApplicationFailure.newFromValues(
103-
failure.getMessage(),
104-
info.getType(),
105-
info.getNonRetryable(),
106-
new EncodedValues(details, dataConverter),
107-
cause,
108-
info.hasNextRetryDelay()
109-
? ProtobufTimeUtils.toJavaDuration(info.getNextRetryDelay())
110-
: null,
111-
FailureUtils.categoryFromProto(info.getCategory()));
102+
return ApplicationFailure.newBuilder()
103+
.setMessage(failure.getMessage())
104+
.setType(info.getType())
105+
.setNonRetryable(info.getNonRetryable())
106+
.setDetails(new EncodedValues(details, dataConverter))
107+
.setCause(cause)
108+
.setNextRetryDelay(
109+
info.hasNextRetryDelay()
110+
? ProtobufTimeUtils.toJavaDuration(info.getNextRetryDelay())
111+
: null)
112+
.setCategory(FailureUtils.categoryFromProto(info.getCategory()))
113+
.build();
112114
}
113115
case TIMEOUT_FAILURE_INFO:
114116
{
@@ -148,14 +150,12 @@ private RuntimeException failureToExceptionImpl(Failure failure, DataConverter d
148150
info.hasLastHeartbeatDetails()
149151
? Optional.of(info.getLastHeartbeatDetails())
150152
: Optional.empty();
151-
return ApplicationFailure.newFromValues(
152-
failure.getMessage(),
153-
"ResetWorkflow",
154-
false,
155-
new EncodedValues(details, dataConverter),
156-
cause,
157-
null,
158-
ApplicationErrorCategory.UNSPECIFIED);
153+
return ApplicationFailure.newBuilder()
154+
.setMessage(failure.getMessage())
155+
.setType("ResetWorkflow")
156+
.setDetails(new EncodedValues(details, dataConverter))
157+
.setCause(cause)
158+
.build();
159159
}
160160
case ACTIVITY_FAILURE_INFO:
161161
{
@@ -211,14 +211,12 @@ private RuntimeException failureToExceptionImpl(Failure failure, DataConverter d
211211
case FAILUREINFO_NOT_SET:
212212
default:
213213
// All unknown types are considered to be retryable ApplicationError.
214-
return ApplicationFailure.newFromValues(
215-
failure.getMessage(),
216-
"",
217-
false,
218-
new EncodedValues(Optional.empty(), dataConverter),
219-
cause,
220-
null,
221-
ApplicationErrorCategory.UNSPECIFIED);
214+
return ApplicationFailure.newBuilder()
215+
.setMessage(failure.getMessage())
216+
.setType("")
217+
.setDetails(new EncodedValues(Optional.empty(), dataConverter))
218+
.setCause(cause)
219+
.build();
222220
}
223221
}
224222

temporal-sdk/src/test/java/io/temporal/internal/worker/ActivityFailedMetricsTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,11 @@ public void execute(boolean isBenign) {
111111
if (!isBenign) {
112112
throw ApplicationFailure.newFailure("Non-benign activity failure", "NonBenignType");
113113
} else {
114-
throw ApplicationFailure.newFailureWithCategory(
115-
"Benign activity failure", "BenignType", ApplicationErrorCategory.BENIGN, null);
114+
throw ApplicationFailure.newBuilder()
115+
.setMessage("Benign activity failure")
116+
.setType("BenignType")
117+
.setCategory(ApplicationErrorCategory.BENIGN)
118+
.build();
116119
}
117120
}
118121
}

temporal-sdk/src/test/java/io/temporal/internal/worker/WorkflowFailedMetricsTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,11 @@ public void execute(boolean isBenign) {
130130
if (!isBenign) {
131131
throw ApplicationFailure.newFailure("Non-benign failure", "NonBenignType");
132132
} else {
133-
throw ApplicationFailure.newFailureWithCategory(
134-
"Benign failure", "BenignType", ApplicationErrorCategory.BENIGN, null);
133+
throw ApplicationFailure.newBuilder()
134+
.setMessage("Benign failure")
135+
.setType("BenignType")
136+
.setCategory(ApplicationErrorCategory.BENIGN)
137+
.build();
135138
}
136139
}
137140
}

0 commit comments

Comments
 (0)