/
Contexts.java
152 lines (138 loc) · 4.59 KB
/
Contexts.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
/*
* Copyright 2015 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;
import com.google.common.base.Preconditions;
import java.util.concurrent.TimeoutException;
/**
* Utility methods for working with {@link Context}s in GRPC.
*/
public final class Contexts {
private Contexts() {
}
/**
* Make the provided {@link Context} {@link Context#current()} for the creation of a listener
* to a received call and for all events received by that listener.
*
* <p>This utility is expected to be used by {@link ServerInterceptor} implementations that need
* to augment the {@link Context} in which the application does work when receiving events from
* the client.
*
* @param context to make {@link Context#current()}.
* @param call used to send responses to client.
* @param headers received from client.
* @param next handler used to create the listener to be wrapped.
* @return listener that will receive events in the scope of the provided context.
*/
public static <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
Context context,
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
Context previous = context.attach();
try {
return new ContextualizedServerCallListener<>(
next.startCall(call, headers),
context);
} finally {
context.detach(previous);
}
}
/**
* Implementation of {@link io.grpc.ForwardingServerCallListener} that attaches a context before
* dispatching calls to the delegate and detaches them after the call completes.
*/
private static class ContextualizedServerCallListener<ReqT> extends
ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
private final Context context;
public ContextualizedServerCallListener(ServerCall.Listener<ReqT> delegate, Context context) {
super(delegate);
this.context = context;
}
@Override
public void onMessage(ReqT message) {
Context previous = context.attach();
try {
super.onMessage(message);
} finally {
context.detach(previous);
}
}
@Override
public void onHalfClose() {
Context previous = context.attach();
try {
super.onHalfClose();
} finally {
context.detach(previous);
}
}
@Override
public void onCancel() {
Context previous = context.attach();
try {
super.onCancel();
} finally {
context.detach(previous);
}
}
@Override
public void onComplete() {
Context previous = context.attach();
try {
super.onComplete();
} finally {
context.detach(previous);
}
}
@Override
public void onReady() {
Context previous = context.attach();
try {
super.onReady();
} finally {
context.detach(previous);
}
}
}
/**
* Returns the {@link Status} of a cancelled context or {@code null} if the context
* is not cancelled.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1975")
public static Status statusFromCancelled(Context context) {
Preconditions.checkNotNull(context, "context must not be null");
if (!context.isCancelled()) {
return null;
}
Throwable cancellationCause = context.cancellationCause();
if (cancellationCause == null) {
return Status.CANCELLED.withDescription("io.grpc.Context was cancelled without error");
}
if (cancellationCause instanceof TimeoutException) {
return Status.DEADLINE_EXCEEDED
.withDescription(cancellationCause.getMessage())
.withCause(cancellationCause);
}
Status status = Status.fromThrowable(cancellationCause);
if (Status.Code.UNKNOWN.equals(status.getCode())
&& status.getCause() == cancellationCause) {
// If fromThrowable could not determine a status, then
// just return CANCELLED.
return Status.CANCELLED.withDescription("Context cancelled").withCause(cancellationCause);
}
return status.withCause(cancellationCause);
}
}