1 /* 2 * Copyright 2016 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.errorhandling; 18 19 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 20 21 import com.google.common.base.Verify; 22 import com.google.common.base.VerifyException; 23 import com.google.common.util.concurrent.FutureCallback; 24 import com.google.common.util.concurrent.Futures; 25 import com.google.common.util.concurrent.ListenableFuture; 26 import com.google.common.util.concurrent.Uninterruptibles; 27 import com.google.rpc.DebugInfo; 28 import io.grpc.CallOptions; 29 import io.grpc.ClientCall; 30 import io.grpc.Grpc; 31 import io.grpc.InsecureChannelCredentials; 32 import io.grpc.InsecureServerCredentials; 33 import io.grpc.ManagedChannel; 34 import io.grpc.Metadata; 35 import io.grpc.Server; 36 import io.grpc.Status; 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.ProtoUtils; 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 setting and reading RPC error details. 52 * Proto used here is just an example proto, but the pattern sending 53 * application error information as an application-specific binary protos 54 * in the response trailers is the recommended way to return application 55 * level error. 56 */ 57 public class DetailErrorSample { 58 private static final Metadata.Key<DebugInfo> DEBUG_INFO_TRAILER_KEY = 59 ProtoUtils.keyForProto(DebugInfo.getDefaultInstance()); 60 61 private static final DebugInfo DEBUG_INFO = 62 DebugInfo.newBuilder() 63 .addStackEntries("stack_entry_1") 64 .addStackEntries("stack_entry_2") 65 .addStackEntries("stack_entry_3") 66 .setDetail("detailed error info.").build(); 67 68 private static final String DEBUG_DESC = "detailed error description"; 69 main(String[] args)70 public static void main(String[] args) throws Exception { 71 new DetailErrorSample().run(); 72 } 73 74 private ManagedChannel channel; 75 run()76 void run() throws Exception { 77 Server server = Grpc.newServerBuilderForPort(0, InsecureServerCredentials.create()) 78 .addService(new GreeterGrpc.GreeterImplBase() { 79 @Override 80 public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) { 81 Metadata trailers = new Metadata(); 82 trailers.put(DEBUG_INFO_TRAILER_KEY, DEBUG_INFO); 83 responseObserver.onError(Status.INTERNAL.withDescription(DEBUG_DESC) 84 .asRuntimeException(trailers)); 85 } 86 }).build().start(); 87 channel = Grpc.newChannelBuilderForAddress( 88 "localhost", server.getPort(), InsecureChannelCredentials.create()).build(); 89 90 blockingCall(); 91 futureCallDirect(); 92 futureCallCallback(); 93 asyncCall(); 94 advancedAsyncCall(); 95 96 channel.shutdown(); 97 server.shutdown(); 98 channel.awaitTermination(1, TimeUnit.SECONDS); 99 server.awaitTermination(); 100 } 101 verifyErrorReply(Throwable t)102 static void verifyErrorReply(Throwable t) { 103 Status status = Status.fromThrowable(t); 104 Metadata trailers = Status.trailersFromThrowable(t); 105 Verify.verify(status.getCode() == Status.Code.INTERNAL); 106 Verify.verify(trailers.containsKey(DEBUG_INFO_TRAILER_KEY)); 107 Verify.verify(status.getDescription().equals(DEBUG_DESC)); 108 try { 109 Verify.verify(trailers.get(DEBUG_INFO_TRAILER_KEY).equals(DEBUG_INFO)); 110 } catch (IllegalArgumentException e) { 111 throw new VerifyException(e); 112 } 113 } 114 blockingCall()115 void blockingCall() { 116 GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); 117 try { 118 stub.sayHello(HelloRequest.newBuilder().build()); 119 } catch (Exception e) { 120 verifyErrorReply(e); 121 } 122 } 123 futureCallDirect()124 void futureCallDirect() { 125 GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel); 126 ListenableFuture<HelloReply> response = 127 stub.sayHello(HelloRequest.newBuilder().build()); 128 129 try { 130 response.get(); 131 } catch (InterruptedException e) { 132 Thread.currentThread().interrupt(); 133 throw new RuntimeException(e); 134 } catch (ExecutionException e) { 135 verifyErrorReply(e.getCause()); 136 } 137 } 138 futureCallCallback()139 void futureCallCallback() { 140 GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel); 141 ListenableFuture<HelloReply> response = 142 stub.sayHello(HelloRequest.newBuilder().build()); 143 144 final CountDownLatch latch = new CountDownLatch(1); 145 146 Futures.addCallback( 147 response, 148 new FutureCallback<HelloReply>() { 149 @Override 150 public void onSuccess(@Nullable HelloReply result) { 151 // Won't be called, since the server in this example always fails. 152 } 153 154 @Override 155 public void onFailure(Throwable t) { 156 verifyErrorReply(t); 157 latch.countDown(); 158 } 159 }, 160 directExecutor()); 161 162 if (!Uninterruptibles.awaitUninterruptibly(latch, 1, TimeUnit.SECONDS)) { 163 throw new RuntimeException("timeout!"); 164 } 165 } 166 asyncCall()167 void asyncCall() { 168 GreeterStub stub = GreeterGrpc.newStub(channel); 169 HelloRequest request = HelloRequest.newBuilder().build(); 170 final CountDownLatch latch = new CountDownLatch(1); 171 StreamObserver<HelloReply> responseObserver = new StreamObserver<HelloReply>() { 172 173 @Override 174 public void onNext(HelloReply value) { 175 // Won't be called. 176 } 177 178 @Override 179 public void onError(Throwable t) { 180 verifyErrorReply(t); 181 latch.countDown(); 182 } 183 184 @Override 185 public void onCompleted() { 186 // Won't be called, since the server in this example always fails. 187 } 188 }; 189 stub.sayHello(request, responseObserver); 190 191 if (!Uninterruptibles.awaitUninterruptibly(latch, 1, TimeUnit.SECONDS)) { 192 throw new RuntimeException("timeout!"); 193 } 194 } 195 196 197 /** 198 * This is more advanced and does not make use of the stub. You should not normally need to do 199 * this, but here is how you would. 200 */ advancedAsyncCall()201 void advancedAsyncCall() { 202 ClientCall<HelloRequest, HelloReply> call = 203 channel.newCall(GreeterGrpc.getSayHelloMethod(), CallOptions.DEFAULT); 204 205 final CountDownLatch latch = new CountDownLatch(1); 206 207 call.start(new ClientCall.Listener<HelloReply>() { 208 209 @Override 210 public void onClose(Status status, Metadata trailers) { 211 Verify.verify(status.getCode() == Status.Code.INTERNAL); 212 Verify.verify(trailers.containsKey(DEBUG_INFO_TRAILER_KEY)); 213 try { 214 Verify.verify(trailers.get(DEBUG_INFO_TRAILER_KEY).equals(DEBUG_INFO)); 215 } catch (IllegalArgumentException e) { 216 throw new VerifyException(e); 217 } 218 219 latch.countDown(); 220 } 221 }, new Metadata()); 222 223 call.sendMessage(HelloRequest.newBuilder().build()); 224 call.halfClose(); 225 226 if (!Uninterruptibles.awaitUninterruptibly(latch, 1, TimeUnit.SECONDS)) { 227 throw new RuntimeException("timeout!"); 228 } 229 } 230 } 231 232