• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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.examples.errordetails;
18 
19 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
20 
21 import com.google.common.base.Verify;
22 import com.google.common.util.concurrent.FutureCallback;
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.ListenableFuture;
25 import com.google.common.util.concurrent.Uninterruptibles;
26 import com.google.protobuf.Any;
27 import com.google.protobuf.InvalidProtocolBufferException;
28 import com.google.rpc.Code;
29 import com.google.rpc.DebugInfo;
30 import com.google.rpc.Status;
31 import io.grpc.Channel;
32 import io.grpc.Grpc;
33 import io.grpc.InsecureChannelCredentials;
34 import io.grpc.InsecureServerCredentials;
35 import io.grpc.ManagedChannel;
36 import io.grpc.Server;
37 import io.grpc.examples.helloworld.GreeterGrpc;
38 import io.grpc.examples.helloworld.GreeterGrpc.GreeterBlockingStub;
39 import io.grpc.examples.helloworld.GreeterGrpc.GreeterFutureStub;
40 import io.grpc.examples.helloworld.GreeterGrpc.GreeterStub;
41 import io.grpc.examples.helloworld.HelloReply;
42 import io.grpc.examples.helloworld.HelloRequest;
43 import io.grpc.protobuf.StatusProto;
44 import io.grpc.stub.StreamObserver;
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.ExecutionException;
47 import java.util.concurrent.TimeUnit;
48 import javax.annotation.Nullable;
49 
50 /**
51  * Shows how to set and read com.google.rpc.Status objects as google.rpc.Status error details.
52  */
53 public class ErrorDetailsExample {
54   private static final DebugInfo DEBUG_INFO =
55       DebugInfo.newBuilder()
56           .addStackEntries("stack_entry_1")
57           .addStackEntries("stack_entry_2")
58           .addStackEntries("stack_entry_3")
59           .setDetail("detailed error info.").build();
60 
main(String[] args)61   public static void main(String[] args) throws Exception {
62     Server server = null;
63     ManagedChannel channel = null;
64 
65     try {
66       server = launchServer();
67       channel = Grpc.newChannelBuilderForAddress(
68           "localhost", server.getPort(), InsecureChannelCredentials.create()).build();
69 
70       runClientTests(channel);
71     } finally {
72       cleanup(channel, server);
73     }
74   }
75 
76 
77   /**
78    * Create server and start it
79    */
launchServer()80   static Server launchServer() throws Exception {
81     return Grpc.newServerBuilderForPort(0, InsecureServerCredentials.create())
82         .addService(new GreeterGrpc.GreeterImplBase() {
83           @Override
84           public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
85             // This is com.google.rpc.Status, not io.grpc.Status
86             Status status = Status.newBuilder()
87                 .setCode(Code.INVALID_ARGUMENT.getNumber())
88                 .setMessage("Email or password malformed")
89                 .addDetails(Any.pack(DEBUG_INFO))
90                 .build();
91             responseObserver.onError(StatusProto.toStatusRuntimeException(status));
92           }
93         })
94         .build()
95         .start();
96   }
97 
98   private static void runClientTests(Channel channel) {
99     blockingCall(channel);
100     futureCallDirect(channel);
101     futureCallCallback(channel);
102     asyncCall(channel);
103   }
104 
105   private static void cleanup(ManagedChannel channel, Server server) throws InterruptedException {
106 
107     // Shutdown client and server for resources to be cleanly released
108     if (channel != null) {
109       channel.shutdown();
110     }
111     if (server != null) {
112       server.shutdown();
113     }
114 
115     // Wait for cleanup to complete
116     if (channel != null) {
117       channel.awaitTermination(1, TimeUnit.SECONDS);
118     }
119     if (server != null) {
120       server.awaitTermination(1, TimeUnit.SECONDS);
121     }
122   }
123 
124   static void verifyErrorReply(Throwable t) {
125     Status status = StatusProto.fromThrowable(t);
126     Verify.verify(status.getCode() == Code.INVALID_ARGUMENT.getNumber());
127     Verify.verify(status.getMessage().equals("Email or password malformed"));
128     try {
129       DebugInfo unpackedDetail = status.getDetails(0).unpack(DebugInfo.class);
130       Verify.verify(unpackedDetail.equals(DEBUG_INFO));
131     } catch (InvalidProtocolBufferException e) {
132       Verify.verify(false, "Message was a different type than expected");
133     }
134   }
135 
136   static void blockingCall(Channel channel) {
137     GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);
138     try {
139       stub.sayHello(HelloRequest.newBuilder().build());
140     } catch (Exception e) {
141       verifyErrorReply(e);
142       System.out.println("Blocking call received expected error details");
143     }
144   }
145 
146   static void futureCallDirect(Channel channel) {
147     GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel);
148     ListenableFuture<HelloReply> response =
149         stub.sayHello(HelloRequest.newBuilder().build());
150 
151     try {
152       response.get();
153     } catch (InterruptedException e) {
154       Thread.currentThread().interrupt();
155       throw new RuntimeException(e);
156     } catch (ExecutionException e) {
157       verifyErrorReply(e.getCause());
158       System.out.println("Future call direct received expected error details");
159     }
160   }
161 
162   static void futureCallCallback(Channel channel) {
163     GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel);
164     ListenableFuture<HelloReply> response =
165         stub.sayHello(HelloRequest.newBuilder().build());
166 
167     final CountDownLatch latch = new CountDownLatch(1);
168 
169     Futures.addCallback(
170         response,
171         new FutureCallback<HelloReply>() {
172           @Override
173           public void onSuccess(@Nullable HelloReply result) {
174             // Won't be called, since the server in this example always fails.
175           }
176 
177           @Override
178           public void onFailure(Throwable t) {
179             verifyErrorReply(t);
180             System.out.println("Future callback received expected error details");
181             latch.countDown();
182           }
183         },
184         directExecutor());
185 
186     if (!Uninterruptibles.awaitUninterruptibly(latch, 1, TimeUnit.SECONDS)) {
187       throw new RuntimeException("timeout!");
188     }
189   }
190 
191   static void asyncCall(Channel channel) {
192     GreeterStub stub = GreeterGrpc.newStub(channel);
193     HelloRequest request = HelloRequest.newBuilder().build();
194     final CountDownLatch latch = new CountDownLatch(1);
195     StreamObserver<HelloReply> responseObserver = new StreamObserver<HelloReply>() {
196 
197       @Override
198       public void onNext(HelloReply value) {
199         // Won't be called.
200       }
201 
202       @Override
203       public void onError(Throwable t) {
204         verifyErrorReply(t);
205         System.out.println("Async call received expected error details");
206         latch.countDown();
207       }
208 
209       @Override
210       public void onCompleted() {
211         // Won't be called, since the server in this example always fails.
212       }
213     };
214     stub.sayHello(request, responseObserver);
215 
216     if (!Uninterruptibles.awaitUninterruptibly(latch, 1, TimeUnit.SECONDS)) {
217       throw new RuntimeException("timeout!");
218     }
219   }
220 }
221 
222