blob: 5566ba157191c38ed160027aa3aeb2ae8e87071b [file] [log] [blame]
lryan56e307f2014-12-05 13:25:08 -08001/*
2 * Copyright 2014, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
nmittlerf8314582015-01-27 10:25:39 -080032package io.grpc;
ejona07d3f6a2014-05-14 11:26:57 -070033
lryanc5e70c22014-11-24 16:41:02 -080034import com.google.common.base.MoreObjects;
ejona8c76c8a2014-10-09 09:53:41 -070035import com.google.common.base.Objects;
ejona07d3f6a2014-05-14 11:26:57 -070036import com.google.common.base.Preconditions;
lryan2ce84462014-06-02 14:43:36 -070037import com.google.common.base.Throwables;
ejona07d3f6a2014-05-14 11:26:57 -070038
simonma828f9412015-01-12 18:08:15 -080039import java.util.ArrayList;
lryan71e4a922014-09-25 18:25:54 -070040import java.util.List;
lryan71e4a922014-09-25 18:25:54 -070041import java.util.TreeMap;
ejona9d502992014-09-22 12:23:19 -070042
ejona07d3f6a2014-05-14 11:26:57 -070043import javax.annotation.Nullable;
44import javax.annotation.concurrent.Immutable;
45
46/**
47 * Defines the status of an operation using the canonical error space.
48 */
49@Immutable
lryan71e4a922014-09-25 18:25:54 -070050public final class Status {
51
52 /**
53 * The set of canonical error codes. If new codes are added over time they must choose
54 * a numerical value that does not collide with any previously defined code.
55 */
56 public enum Code {
57 OK(0),
58
59 // The operation was cancelled (typically by the caller).
60 CANCELLED(1),
61
62 // Unknown error. An example of where this error may be returned is
63 // if a Status value received from another address space belongs to
64 // an error-space that is not known in this address space. Also
65 // errors raised by APIs that do not return enough error information
66 // may be converted to this error.
67 UNKNOWN(2),
68
69 // Client specified an invalid argument. Note that this differs
70 // from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
71 // that are problematic regardless of the state of the system
72 // (e.g., a malformed file name).
73 INVALID_ARGUMENT(3),
74
75 // Deadline expired before operation could complete. For operations
76 // that change the state of the system, this error may be returned
77 // even if the operation has completed successfully. For example, a
78 // successful response from a server could have been delayed long
79 // enough for the deadline to expire.
80 DEADLINE_EXCEEDED(4),
81
82 // Some requested entity (e.g., file or directory) was not found.
83 NOT_FOUND(5),
84
85 // Some entity that we attempted to create (e.g., file or directory)
86 // already exists.
87 ALREADY_EXISTS(6),
88
89 // The caller does not have permission to execute the specified
90 // operation. PERMISSION_DENIED must not be used for rejections
91 // caused by exhausting some resource (use RESOURCE_EXHAUSTED
92 // instead for those errors). PERMISSION_DENIED must not be
93 // used if the caller cannot be identified (use UNAUTHENTICATED
94 // instead for those errors).
95 PERMISSION_DENIED(7),
96
97 // Some resource has been exhausted, perhaps a per-user quota, or
98 // perhaps the entire file system is out of space.
99 RESOURCE_EXHAUSTED(8),
100
101 // Operation was rejected because the system is not in a state
102 // required for the operation's execution. For example, directory
103 // to be deleted may be non-empty, an rmdir operation is applied to
104 // a non-directory, etc.
105 //
106 // A litmus test that may help a service implementor in deciding
107 // between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
108 // (a) Use UNAVAILABLE if the client can retry just the failing call.
109 // (b) Use ABORTED if the client should retry at a higher-level
110 // (e.g., restarting a read-modify-write sequence).
111 // (c) Use FAILED_PRECONDITION if the client should not retry until
112 // the system state has been explicitly fixed. E.g., if an "rmdir"
113 // fails because the directory is non-empty, FAILED_PRECONDITION
114 // should be returned since the client should not retry unless
115 // they have first fixed up the directory by deleting files from it.
116 FAILED_PRECONDITION(9),
117
118 // The operation was aborted, typically due to a concurrency issue
119 // like sequencer check failures, transaction aborts, etc.
120 //
121 // See litmus test above for deciding between FAILED_PRECONDITION,
122 // ABORTED, and UNAVAILABLE.
123 ABORTED(10),
124
125 // Operation was attempted past the valid range. E.g., seeking or
126 // reading past end of file.
127 //
128 // Unlike INVALID_ARGUMENT, this error indicates a problem that may
129 // be fixed if the system state changes. For example, a 32-bit file
130 // system will generate INVALID_ARGUMENT if asked to read at an
131 // offset that is not in the range [0,2^32-1], but it will generate
132 // OUT_OF_RANGE if asked to read from an offset past the current
133 // file size.
134 //
135 // There is a fair bit of overlap between FAILED_PRECONDITION and
136 // OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific
137 // error) when it applies so that callers who are iterating through
138 // a space can easily look for an OUT_OF_RANGE error to detect when
139 // they are done.
140 OUT_OF_RANGE(11),
141
142 // Operation is not implemented or not supported/enabled in this service.
143 UNIMPLEMENTED(12),
144
145 // Internal errors. Means some invariants expected by underlying
146 // system has been broken. If you see one of these errors,
147 // something is very broken.
148 INTERNAL(13),
149
150 // The service is currently unavailable. This is a most likely a
151 // transient condition and may be corrected by retrying with
152 // a backoff.
153 //
154 // See litmus test above for deciding between FAILED_PRECONDITION,
155 // ABORTED, and UNAVAILABLE.
156 UNAVAILABLE(14),
157
158 // Unrecoverable data loss or corruption.
159 DATA_LOSS(15),
160
161 // The request does not have valid authentication credentials for the
162 // operation.
163 UNAUTHENTICATED(16);
164
165 private final int value;
ejona0a585332014-10-23 18:47:40 -0700166 private final String valueAscii;
lryan71e4a922014-09-25 18:25:54 -0700167
168 private Code(int value) {
169 this.value = value;
ejona0a585332014-10-23 18:47:40 -0700170 this.valueAscii = Integer.toString(value);
lryan71e4a922014-09-25 18:25:54 -0700171 }
172
173 public int value() {
174 return value;
175 }
scottpeterson8f6e2c22014-09-26 17:09:39 -0700176
lryan71e4a922014-09-25 18:25:54 -0700177 private Status status() {
178 return STATUS_LIST.get(value);
179 }
ejona0a585332014-10-23 18:47:40 -0700180
181 private String valueAscii() {
182 return valueAscii;
183 }
lryan71e4a922014-09-25 18:25:54 -0700184 }
185
186 // Create the canonical list of Status instances indexed by their code values.
187 private static List<Status> STATUS_LIST;
188 static {
ejona8c76c8a2014-10-09 09:53:41 -0700189 TreeMap<Integer, Status> canonicalizer = new TreeMap<Integer, Status>();
lryan71e4a922014-09-25 18:25:54 -0700190 for (Code code : Code.values()) {
191 Status replaced = canonicalizer.put(code.value(), new Status(code));
192 if (replaced != null) {
193 throw new IllegalStateException("Code value duplication between " +
194 replaced.getCode().name() + " & " + code.name());
195 }
196 }
simonma828f9412015-01-12 18:08:15 -0800197 STATUS_LIST = new ArrayList<Status>(canonicalizer.values());
lryan71e4a922014-09-25 18:25:54 -0700198 }
199
200 // A pseudo-enum of Status instances mapped 1:1 with values in Code. This simplifies construction
201 // patterns for derived implementations of Status.
202 public static final Status OK = Code.OK.status();
203 public static final Status CANCELLED = Code.CANCELLED.status();
204 public static final Status UNKNOWN = Code.UNKNOWN.status();
205 public static final Status INVALID_ARGUMENT = Code.INVALID_ARGUMENT.status();
206 public static final Status DEADLINE_EXCEEDED = Code.DEADLINE_EXCEEDED.status();
207 public static final Status NOT_FOUND = Code.NOT_FOUND.status();
208 public static final Status ALREADY_EXISTS = Code.ALREADY_EXISTS.status();
209 public static final Status PERMISSION_DENIED = Code.PERMISSION_DENIED.status();
scottpeterson8f6e2c22014-09-26 17:09:39 -0700210 public static final Status UNAUTHENTICATED = Code.UNAUTHENTICATED.status();
lryan71e4a922014-09-25 18:25:54 -0700211 public static final Status RESOURCE_EXHAUSTED = Code.RESOURCE_EXHAUSTED.status();
scottpeterson8f6e2c22014-09-26 17:09:39 -0700212 public static final Status FAILED_PRECONDITION =
lryan71e4a922014-09-25 18:25:54 -0700213 Code.FAILED_PRECONDITION.status();
214 public static final Status ABORTED = Code.ABORTED.status();
215 public static final Status OUT_OF_RANGE = Code.OUT_OF_RANGE.status();
216 public static final Status UNIMPLEMENTED = Code.UNIMPLEMENTED.status();
217 public static final Status INTERNAL = Code.INTERNAL.status();
218 public static final Status UNAVAILABLE = Code.UNAVAILABLE.status();
219 public static final Status DATA_LOSS = Code.DATA_LOSS.status();
220
221 /**
222 * Return a {@link Status} given a canonical error {@link Code} value.
223 */
224 public static Status fromCodeValue(int codeValue) {
lryan71e4a922014-09-25 18:25:54 -0700225 if (codeValue < 0 || codeValue > STATUS_LIST.size()) {
226 return UNKNOWN.withDescription("Unknown code " + codeValue);
227 } else {
nathanmittler0304b3d2014-10-24 13:39:13 -0700228 return STATUS_LIST.get(codeValue);
lryan71e4a922014-09-25 18:25:54 -0700229 }
230 }
231
232 /**
233 * Key to bind status code to trailers.
234 */
235 public static final Metadata.Key<Status> CODE_KEY
236 = Metadata.Key.of("grpc-status", new StatusCodeMarshaller());
237
238 /**
239 * Key to bind status message to trailers.
240 */
ejona9d502992014-09-22 12:23:19 -0700241 public static final Metadata.Key<String> MESSAGE_KEY
zhangkun2b116ef2014-12-03 20:28:00 -0800242 = Metadata.Key.of("grpc-message", Metadata.ASCII_STRING_MARSHALLER);
ejona9d502992014-09-22 12:23:19 -0700243
lryan71e4a922014-09-25 18:25:54 -0700244 /**
245 * Extract an error {@link Status} from the causal chain of a {@link Throwable}.
246 */
lryan2ce84462014-06-02 14:43:36 -0700247 public static Status fromThrowable(Throwable t) {
248 for (Throwable cause : Throwables.getCausalChain(t)) {
249 if (cause instanceof OperationException) {
250 return ((Status.OperationException) cause).getStatus();
251 } else if (cause instanceof OperationRuntimeException) {
252 return ((Status.OperationRuntimeException) cause).getStatus();
253 }
254 }
255 // Couldn't find a cause with a Status
lryan71e4a922014-09-25 18:25:54 -0700256 return INTERNAL.withCause(t);
lryan2ce84462014-06-02 14:43:36 -0700257 }
258
lryan71e4a922014-09-25 18:25:54 -0700259 private final Code code;
ejona07d3f6a2014-05-14 11:26:57 -0700260 private final String description;
261 private final Throwable cause;
262
lryan71e4a922014-09-25 18:25:54 -0700263 private Status(Code code) {
ejona07d3f6a2014-05-14 11:26:57 -0700264 this(code, null, null);
265 }
266
lryan71e4a922014-09-25 18:25:54 -0700267 private Status(Code code, @Nullable String description, @Nullable Throwable cause) {
ejona07d3f6a2014-05-14 11:26:57 -0700268 this.code = Preconditions.checkNotNull(code);
269 this.description = description;
270 this.cause = cause;
271 }
272
lryan71e4a922014-09-25 18:25:54 -0700273 /**
274 * Create a derived instance of {@link Status} with the given cause.
275 */
276 public Status withCause(Throwable cause) {
ejona8c76c8a2014-10-09 09:53:41 -0700277 if (Objects.equal(this.cause, cause)) {
lryan71e4a922014-09-25 18:25:54 -0700278 return this;
279 }
280 return new Status(this.code, this.description, cause);
281 }
282
283 /**
284 * Create a derived instance of {@link Status} with the given description.
285 */
286 public Status withDescription(String description) {
ejona8c76c8a2014-10-09 09:53:41 -0700287 if (Objects.equal(this.description, description)) {
lryan71e4a922014-09-25 18:25:54 -0700288 return this;
289 }
290 return new Status(this.code, description, this.cause);
291 }
292
lryan669724a2014-11-10 10:21:45 -0800293 /**
294 * Create a derived instance of {@link Status} with the given description.
295 */
296 public Status augmentDescription(String additionalDetail) {
297 if (additionalDetail == null) {
298 return this;
299 } else if (this.description == null) {
300 return new Status(this.code, additionalDetail, this.cause);
301 } else {
302 return new Status(this.code, this.description + "\n" + additionalDetail, this.cause);
303 }
304 }
305
lryan71e4a922014-09-25 18:25:54 -0700306 public Code getCode() {
ejona07d3f6a2014-05-14 11:26:57 -0700307 return code;
308 }
309
310 @Nullable
311 public String getDescription() {
312 return description;
313 }
314
315 @Nullable
316 public Throwable getCause() {
317 return cause;
318 }
319
lryan71e4a922014-09-25 18:25:54 -0700320 /**
321 * Is this status OK, i.e. not an error.
322 */
ejona35cabd02014-06-11 14:46:25 -0700323 public boolean isOk() {
lryan71e4a922014-09-25 18:25:54 -0700324 return Code.OK == code;
ejona35cabd02014-06-11 14:46:25 -0700325 }
326
ejona07d3f6a2014-05-14 11:26:57 -0700327 /**
lryan71e4a922014-09-25 18:25:54 -0700328 * Convert this {@link Status} to a {@link RuntimeException}. Use {@code #fromThrowable}
329 * to recover this {@link Status} instance when the returned exception is in the causal chain.
ejona07d3f6a2014-05-14 11:26:57 -0700330 */
ejona07d3f6a2014-05-14 11:26:57 -0700331 public RuntimeException asRuntimeException() {
332 return new OperationRuntimeException(this);
333 }
334
lryan71e4a922014-09-25 18:25:54 -0700335 /**
336 * Convert this {@link Status} to an {@link Exception}. Use {@code #fromThrowable}
337 * to recover this {@link Status} instance when the returned exception is in the causal chain.
338 */
ejona07d3f6a2014-05-14 11:26:57 -0700339 public Exception asException() {
340 return new OperationException(this);
341 }
342
343 /**
344 * Exception thrown by implementations while managing an operation.
345 */
346 public static class OperationException extends Exception {
347
348 private final Status status;
349
350 public OperationException(Status status) {
zhangkune1ae25c2014-08-08 22:39:39 -0700351 super(status.getCode() + ": " + status.getDescription(), status.getCause());
ejona07d3f6a2014-05-14 11:26:57 -0700352 this.status = status;
353 }
354
355 public Status getStatus() {
356 return status;
357 }
358 }
359
360 /**
361 * Runtime exception thrown by implementations while managing an operation.
362 */
363 public static class OperationRuntimeException extends RuntimeException {
364
365 private final Status status;
366
367 public OperationRuntimeException(Status status) {
zhangkune1ae25c2014-08-08 22:39:39 -0700368 super(status.getCode() + ": " + status.getDescription(), status.getCause());
ejona07d3f6a2014-05-14 11:26:57 -0700369 this.status = status;
370 }
371
372 public Status getStatus() {
373 return status;
374 }
375 }
zhangkun347a22d2014-05-21 16:44:20 -0700376
377 @Override
378 public String toString() {
lryanc5e70c22014-11-24 16:41:02 -0800379 return MoreObjects.toStringHelper(this)
380 .add("code", code.name())
381 .add("description", description)
382 .add("cause", cause)
383 .toString();
zhangkun347a22d2014-05-21 16:44:20 -0700384 }
ejona9d502992014-09-22 12:23:19 -0700385
zhangkun2b116ef2014-12-03 20:28:00 -0800386 private static class StatusCodeMarshaller implements Metadata.AsciiMarshaller<Status> {
ejona9d502992014-09-22 12:23:19 -0700387 @Override
zhangkun2b116ef2014-12-03 20:28:00 -0800388 public String toAsciiString(Status status) {
ejona0a585332014-10-23 18:47:40 -0700389 return status.getCode().valueAscii();
ejona9d502992014-09-22 12:23:19 -0700390 }
391
392 @Override
zhangkun2b116ef2014-12-03 20:28:00 -0800393 public Status parseAsciiString(String serialized) {
394 return fromCodeValue(Integer.valueOf(serialized));
ejona9d502992014-09-22 12:23:19 -0700395 }
396 }
ejona07d3f6a2014-05-14 11:26:57 -0700397}