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