forked from dapr/java-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFailureDetails.java
More file actions
151 lines (135 loc) · 5.58 KB
/
FailureDetails.java
File metadata and controls
151 lines (135 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
* Copyright 2025 The Dapr Authors
* Licensed 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 io.dapr.durabletask;
import com.google.protobuf.StringValue;
import io.dapr.durabletask.implementation.protobuf.Orchestration.TaskFailureDetails;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Class that represents the details of a task failure.
*
* <p>In most cases, failures are caused by unhandled exceptions in activity or orchestrator code, in which case
* instances of this class will expose the details of the exception. However, it's also possible that other types
* of errors could result in task failures, in which case there may not be any exception-specific information.</p>
*/
public final class FailureDetails {
private final String errorType;
private final String errorMessage;
private final String stackTrace;
private final boolean isNonRetriable;
FailureDetails(
String errorType,
@Nullable String errorMessage,
@Nullable String errorDetails,
boolean isNonRetriable) {
this.errorType = errorType;
this.stackTrace = errorDetails;
// Error message can be null for things like NullPointerException but the gRPC contract doesn't allow null
this.errorMessage = errorMessage != null ? errorMessage : "";
this.isNonRetriable = isNonRetriable;
}
FailureDetails(Exception exception) {
this(exception.getClass().getName(), exception.getMessage(), getFullStackTrace(exception), false);
}
FailureDetails(TaskFailureDetails proto) {
this(proto.getErrorType(),
proto.getErrorMessage(),
proto.getStackTrace().getValue(),
proto.getIsNonRetriable());
}
/**
* Gets the exception class name if the failure was caused by an unhandled exception. Otherwise, gets a symbolic
* name that describes the general type of error that was encountered.
*
* @return the error type as a {@code String} value
*/
@Nonnull
public String getErrorType() {
return this.errorType;
}
/**
* Gets a summary description of the error that caused this failure. If the failure was caused by an exception, the
* exception message is returned.
*
* @return a summary description of the error
*/
@Nonnull
public String getErrorMessage() {
return this.errorMessage;
}
/**
* Gets the stack trace of the exception that caused this failure, or {@code null} if the failure was caused by
* a non-exception error.
*
* @return the stack trace of the failure exception or {@code null} if the failure was not caused by an exception
*/
@Nullable
public String getStackTrace() {
return this.stackTrace;
}
/**
* Returns {@code true} if the failure doesn't permit retries, otherwise {@code false}.
*
* @return {@code true} if the failure doesn't permit retries, otherwise {@code false}.
*/
public boolean isNonRetriable() {
return this.isNonRetriable;
}
/**
* Returns {@code true} if the task failure was provided by the specified exception type, otherwise {@code false}.
*
* <p>This method allows checking if a task failed due to a specific exception type by attempting to load the class
* specified in {@link #getErrorType()}. If the exception class cannot be loaded for any reason, this method will
* return {@code false}. Base types are supported by this method, as shown in the following example:</p>
* <pre>{@code
* boolean isRuntimeException = failureDetails.isCausedBy(RuntimeException.class);
* }</pre>
*
* @param exceptionClass the class representing the exception type to test
* @return {@code true} if the task failure was provided by the specified exception type, otherwise {@code false}
*/
public boolean isCausedBy(Class<? extends Exception> exceptionClass) {
String actualClassName = this.getErrorType();
try {
// Try using reflection to load the failure's class type and see if it's a subtype of the specified
// exception. For example, this should always succeed if exceptionClass is System.Exception.
Class<?> actualExceptionClass = Class.forName(actualClassName);
return exceptionClass.isAssignableFrom(actualExceptionClass);
} catch (ClassNotFoundException ex) {
// Can't load the class and thus can't tell if it's related
return false;
}
}
/**
* Gets the full stack trace of the specified exception.
*
* @param e the exception
* @return the full stack trace of the exception
*/
public static String getFullStackTrace(Throwable e) {
StackTraceElement[] elements = e.getStackTrace();
// Plan for 256 characters per stack frame (which is likely on the high-end)
StringBuilder sb = new StringBuilder(elements.length * 256);
for (StackTraceElement element : elements) {
sb.append("\tat ").append(element.toString()).append(System.lineSeparator());
}
return sb.toString();
}
TaskFailureDetails toProto() {
return TaskFailureDetails.newBuilder()
.setErrorType(this.getErrorType())
.setErrorMessage(this.getErrorMessage())
.setStackTrace(StringValue.of(this.getStackTrace() != null ? this.getStackTrace() : ""))
.build();
}
}