blob: 61f6ed786f95a16d72b7c9661663dda545d0fad5 [file] [log] [blame]
ejona07d3f6a2014-05-14 11:26:57 -07001package com.google.net.stubby;
2
3import com.google.common.base.Preconditions;
lryan2ce84462014-06-02 14:43:36 -07004import com.google.common.base.Throwables;
ejona07d3f6a2014-05-14 11:26:57 -07005import com.google.net.stubby.transport.Transport;
6
ejona9d502992014-09-22 12:23:19 -07007import java.util.logging.Logger;
8
ejona07d3f6a2014-05-14 11:26:57 -07009import javax.annotation.Nullable;
10import javax.annotation.concurrent.Immutable;
11
12/**
13 * Defines the status of an operation using the canonical error space.
14 */
15@Immutable
16public class Status {
ejona07d3f6a2014-05-14 11:26:57 -070017 public static final Status OK = new Status(Transport.Code.OK);
nathanmittlercc7cdb12014-07-11 12:00:32 -070018 public static final Status CANCELLED = new Status(Transport.Code.CANCELLED);
ejona9d502992014-09-22 12:23:19 -070019 public static final Metadata.Key<Transport.Code> CODE_KEY
20 = Metadata.Key.of("grpc-status", new CodeMarshaller());
21 public static final Metadata.Key<String> MESSAGE_KEY
22 = Metadata.Key.of("grpc-message", Metadata.STRING_MARSHALLER);
23
24 private static final Logger log = Logger.getLogger(Status.class.getName());
ejona07d3f6a2014-05-14 11:26:57 -070025
lryan2ce84462014-06-02 14:43:36 -070026 public static Status fromThrowable(Throwable t) {
27 for (Throwable cause : Throwables.getCausalChain(t)) {
28 if (cause instanceof OperationException) {
29 return ((Status.OperationException) cause).getStatus();
30 } else if (cause instanceof OperationRuntimeException) {
31 return ((Status.OperationRuntimeException) cause).getStatus();
32 }
33 }
34 // Couldn't find a cause with a Status
35 return new Status(Transport.Code.INTERNAL, t);
36 }
37
ejona07d3f6a2014-05-14 11:26:57 -070038 private final Transport.Code code;
39 private final String description;
40 private final Throwable cause;
41
42 public Status(Transport.Code code) {
43 this(code, null, null);
44 }
45
46 public Status(Transport.Code code, @Nullable String description) {
47 this(code, description, null);
48 }
49
50 public Status(Transport.Code code, @Nullable Throwable cause) {
51 this(code, null, cause);
52 }
53
54 public Status(Transport.Code code, @Nullable String description, @Nullable Throwable cause) {
55 this.code = Preconditions.checkNotNull(code);
56 this.description = description;
57 this.cause = cause;
58 }
59
60 public Transport.Code getCode() {
61 return code;
62 }
63
64 @Nullable
65 public String getDescription() {
66 return description;
67 }
68
69 @Nullable
70 public Throwable getCause() {
71 return cause;
72 }
73
ejona35cabd02014-06-11 14:46:25 -070074 public boolean isOk() {
75 return OK.getCode() == getCode();
76 }
77
ejona07d3f6a2014-05-14 11:26:57 -070078 /**
79 * Override this status with another if allowed.
80 */
81 public Status overrideWith(Status newStatus) {
82 if (this.getCode() == Transport.Code.OK || newStatus.code == Transport.Code.OK) {
83 return this;
84 } else {
85 return newStatus;
86 }
87 }
88
89 public RuntimeException asRuntimeException() {
90 return new OperationRuntimeException(this);
91 }
92
93 public Exception asException() {
94 return new OperationException(this);
95 }
96
97 /**
98 * Exception thrown by implementations while managing an operation.
99 */
100 public static class OperationException extends Exception {
101
102 private final Status status;
103
104 public OperationException(Status status) {
zhangkune1ae25c2014-08-08 22:39:39 -0700105 super(status.getCode() + ": " + status.getDescription(), status.getCause());
ejona07d3f6a2014-05-14 11:26:57 -0700106 this.status = status;
107 }
108
109 public Status getStatus() {
110 return status;
111 }
112 }
113
114 /**
115 * Runtime exception thrown by implementations while managing an operation.
116 */
117 public static class OperationRuntimeException extends RuntimeException {
118
119 private final Status status;
120
121 public OperationRuntimeException(Status status) {
zhangkune1ae25c2014-08-08 22:39:39 -0700122 super(status.getCode() + ": " + status.getDescription(), status.getCause());
ejona07d3f6a2014-05-14 11:26:57 -0700123 this.status = status;
124 }
125
126 public Status getStatus() {
127 return status;
128 }
129 }
zhangkun347a22d2014-05-21 16:44:20 -0700130
131 @Override
132 public String toString() {
133 StringBuilder builder = new StringBuilder();
134 builder.append("[").append(code);
135 if (description != null) {
136 builder.append(";").append(description);
137 }
138 if (cause != null) {
139 builder.append(";").append(cause);
140 }
141 builder.append("]");
142 return builder.toString();
143 }
ejona9d502992014-09-22 12:23:19 -0700144
145 private static class CodeMarshaller implements Metadata.Marshaller<Transport.Code> {
146 @Override
147 public byte[] toBytes(Transport.Code value) {
148 return Metadata.INTEGER_MARSHALLER.toBytes(value.getNumber());
149 }
150
151 @Override
152 public String toAscii(Transport.Code value) {
153 return Metadata.INTEGER_MARSHALLER.toAscii(value.getNumber());
154 }
155
156 @Override
157 public Transport.Code parseBytes(byte[] serialized) {
158 return intToCode(Metadata.INTEGER_MARSHALLER.parseBytes(serialized));
159 }
160
161 @Override
162 public Transport.Code parseAscii(String ascii) {
163 return intToCode(Metadata.INTEGER_MARSHALLER.parseAscii(ascii));
164 }
165
166 private Transport.Code intToCode(Integer i) {
167 Transport.Code code = Transport.Code.valueOf(i);
168 if (code == null) {
169 log.warning("Unknown Code: " + i);
170 code = Transport.Code.UNKNOWN;
171 }
172 return code;
173 }
174 }
ejona07d3f6a2014-05-14 11:26:57 -0700175}