forked from grpc/grpc-java
/
StatusProto.java
177 lines (163 loc) · 7.24 KB
/
StatusProto.java
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
* Copyright 2017 The gRPC 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.grpc.protobuf;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import io.grpc.ExperimentalApi;
import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.StatusException;
import io.grpc.StatusRuntimeException;
import io.grpc.protobuf.lite.ProtoLiteUtils;
import javax.annotation.Nullable;
/** Utility methods for working with {@link com.google.rpc.Status}. */
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4695")
public final class StatusProto {
private StatusProto() {}
private static final Metadata.Key<com.google.rpc.Status> STATUS_DETAILS_KEY =
Metadata.Key.of(
"grpc-status-details-bin",
ProtoLiteUtils.metadataMarshaller(com.google.rpc.Status.getDefaultInstance()));
/**
* Convert a {@link com.google.rpc.Status} instance to a {@link StatusRuntimeException}.
*
* <p>The returned {@link StatusRuntimeException} will wrap a {@link Status} whose code and
* description are set from the code and message in {@code statusProto}. {@code statusProto} will
* be serialized and placed into the metadata of the returned {@link StatusRuntimeException}.
*
* @throws IllegalArgumentException if the value of {@code statusProto.getCode()} is not a valid
* gRPC status code.
* @since 1.3.0
*/
public static StatusRuntimeException toStatusRuntimeException(com.google.rpc.Status statusProto) {
return toStatus(statusProto).asRuntimeException(toMetadata(statusProto));
}
/**
* Convert a {@link com.google.rpc.Status} instance to a {@link StatusRuntimeException} with
* additional metadata.
*
* <p>The returned {@link StatusRuntimeException} will wrap a {@link Status} whose code and
* description are set from the code and message in {@code statusProto}. {@code statusProto} will
* be serialized and added to {@code metadata}. {@code metadata} will be set as the metadata of
* the returned {@link StatusRuntimeException}.
*
* @throws IllegalArgumentException if the value of {@code statusProto.getCode()} is not a valid
* gRPC status code.
* @since 1.3.0
*/
public static StatusRuntimeException toStatusRuntimeException(
com.google.rpc.Status statusProto, Metadata metadata) {
return toStatus(statusProto).asRuntimeException(toMetadata(statusProto, metadata));
}
/**
* Convert a {@link com.google.rpc.Status} instance to a {@link StatusException}.
*
* <p>The returned {@link StatusException} will wrap a {@link Status} whose code and description
* are set from the code and message in {@code statusProto}. {@code statusProto} will be
* serialized and placed into the metadata of the returned {@link StatusException}.
*
* @throws IllegalArgumentException if the value of {@code statusProto.getCode()} is not a valid
* gRPC status code.
* @since 1.3.0
*/
public static StatusException toStatusException(com.google.rpc.Status statusProto) {
return toStatus(statusProto).asException(toMetadata(statusProto));
}
/**
* Convert a {@link com.google.rpc.Status} instance to a {@link StatusException} with additional
* metadata.
*
* <p>The returned {@link StatusException} will wrap a {@link Status} whose code and description
* are set from the code and message in {@code statusProto}. {@code statusProto} will be
* serialized and added to {@code metadata}. {@code metadata} will be set as the metadata of the
* returned {@link StatusException}.
*
* @throws IllegalArgumentException if the value of {@code statusProto.getCode()} is not a valid
* gRPC status code.
* @since 1.3.0
*/
public static StatusException toStatusException(
com.google.rpc.Status statusProto, Metadata metadata) {
return toStatus(statusProto).asException(toMetadata(statusProto, metadata));
}
private static Status toStatus(com.google.rpc.Status statusProto) {
Status status = Status.fromCodeValue(statusProto.getCode());
checkArgument(status.getCode().value() == statusProto.getCode(), "invalid status code");
return status.withDescription(statusProto.getMessage());
}
private static Metadata toMetadata(com.google.rpc.Status statusProto) {
Metadata metadata = new Metadata();
metadata.put(STATUS_DETAILS_KEY, statusProto);
return metadata;
}
private static Metadata toMetadata(com.google.rpc.Status statusProto, Metadata metadata) {
checkNotNull(metadata, "metadata must not be null");
metadata.discardAll(STATUS_DETAILS_KEY);
metadata.put(STATUS_DETAILS_KEY, statusProto);
return metadata;
}
/**
* Extract a {@link com.google.rpc.Status} instance from the causal chain of a {@link Throwable}.
*
* @return the extracted {@link com.google.rpc.Status} instance, or {@code null} if none exists.
* @throws IllegalArgumentException if an embedded {@link com.google.rpc.Status} is found and its
* code does not match the gRPC {@link Status} code.
* @since 1.3.0
*/
@Nullable
public static com.google.rpc.Status fromThrowable(Throwable t) {
Throwable cause = checkNotNull(t, "t");
while (cause != null) {
if (cause instanceof StatusException) {
StatusException e = (StatusException) cause;
return fromStatusAndTrailers(e.getStatus(), e.getTrailers());
} else if (cause instanceof StatusRuntimeException) {
StatusRuntimeException e = (StatusRuntimeException) cause;
return fromStatusAndTrailers(e.getStatus(), e.getTrailers());
}
cause = cause.getCause();
}
return null;
}
/**
* Extracts the {@code google.rpc.Status} from trailers, and makes sure they match the gRPC
* {@code status}. If the trailers do not contain a {@code google.rpc.Status}, it uses
* {@code status} param to generate a {@code google.rpc.Status}.
*
* @return the embedded google.rpc.Status
* @since 1.11.0
*/
public static com.google.rpc.Status fromStatusAndTrailers(
Status status, @Nullable Metadata trailers) {
checkNotNull(status, "status");
if (trailers != null) {
com.google.rpc.Status statusProto = trailers.get(STATUS_DETAILS_KEY);
if (statusProto != null) {
checkArgument(
status.getCode().value() == statusProto.getCode(),
"com.google.rpc.Status code must match gRPC status code");
return statusProto;
}
}
// fall-back to status, this is useful if the error is local. e.g. Server is unavailable.
com.google.rpc.Status.Builder statusBuilder = com.google.rpc.Status.newBuilder()
.setCode(status.getCode().value());
if (status.getDescription() != null) {
statusBuilder.setMessage(status.getDescription());
}
return statusBuilder.build();
}
}