• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc;
18 
19 import com.google.common.base.Preconditions;
20 import java.util.concurrent.TimeoutException;
21 
22 /**
23  * Utility methods for working with {@link Context}s in GRPC.
24  */
25 public final class Contexts {
26 
Contexts()27   private Contexts() {
28   }
29 
30   /**
31    * Make the provided {@link Context} {@link Context#current()} for the creation of a listener
32    * to a received call and for all events received by that listener.
33    *
34    * <p>This utility is expected to be used by {@link ServerInterceptor} implementations that need
35    * to augment the {@link Context} in which the application does work when receiving events from
36    * the client.
37    *
38    * @param context to make {@link Context#current()}.
39    * @param call used to send responses to client.
40    * @param headers received from client.
41    * @param next handler used to create the listener to be wrapped.
42    * @return listener that will receive events in the scope of the provided context.
43    */
interceptCall( Context context, ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next)44   public static <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
45         Context context,
46         ServerCall<ReqT, RespT> call,
47         Metadata headers,
48         ServerCallHandler<ReqT, RespT> next) {
49     Context previous = context.attach();
50     try {
51       return new ContextualizedServerCallListener<>(
52           next.startCall(call, headers),
53           context);
54     } finally {
55       context.detach(previous);
56     }
57   }
58 
59   /**
60    * Implementation of {@link io.grpc.ForwardingServerCallListener} that attaches a context before
61    * dispatching calls to the delegate and detaches them after the call completes.
62    */
63   private static class ContextualizedServerCallListener<ReqT> extends
64       ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
65     private final Context context;
66 
ContextualizedServerCallListener(ServerCall.Listener<ReqT> delegate, Context context)67     public ContextualizedServerCallListener(ServerCall.Listener<ReqT> delegate, Context context) {
68       super(delegate);
69       this.context = context;
70     }
71 
72     @Override
onMessage(ReqT message)73     public void onMessage(ReqT message) {
74       Context previous = context.attach();
75       try {
76         super.onMessage(message);
77       } finally {
78         context.detach(previous);
79       }
80     }
81 
82     @Override
onHalfClose()83     public void onHalfClose() {
84       Context previous = context.attach();
85       try {
86         super.onHalfClose();
87       } finally {
88         context.detach(previous);
89       }
90     }
91 
92     @Override
onCancel()93     public void onCancel() {
94       Context previous = context.attach();
95       try {
96         super.onCancel();
97       } finally {
98         context.detach(previous);
99       }
100     }
101 
102     @Override
onComplete()103     public void onComplete() {
104       Context previous = context.attach();
105       try {
106         super.onComplete();
107       } finally {
108         context.detach(previous);
109       }
110     }
111 
112     @Override
onReady()113     public void onReady() {
114       Context previous = context.attach();
115       try {
116         super.onReady();
117       } finally {
118         context.detach(previous);
119       }
120     }
121   }
122 
123   /**
124    * Returns the {@link Status} of a cancelled context or {@code null} if the context
125    * is not cancelled.
126    */
127   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1975")
statusFromCancelled(Context context)128   public static Status statusFromCancelled(Context context) {
129     Preconditions.checkNotNull(context, "context must not be null");
130     if (!context.isCancelled()) {
131       return null;
132     }
133 
134     Throwable cancellationCause = context.cancellationCause();
135     if (cancellationCause == null) {
136       return Status.CANCELLED.withDescription("io.grpc.Context was cancelled without error");
137     }
138     if (cancellationCause instanceof TimeoutException) {
139       return Status.DEADLINE_EXCEEDED
140           .withDescription(cancellationCause.getMessage())
141           .withCause(cancellationCause);
142     }
143     Status status = Status.fromThrowable(cancellationCause);
144     if (Status.Code.UNKNOWN.equals(status.getCode())
145         && status.getCause() == cancellationCause) {
146       // If fromThrowable could not determine a status, then
147       // just return CANCELLED.
148       return Status.CANCELLED.withDescription("Context cancelled").withCause(cancellationCause);
149     }
150     return status.withCause(cancellationCause);
151   }
152 }
153