-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Include error details in unchecked Java exception (#768)
The checked GatewayException already has its printStackTrace augmented with any associated gRPC ErrorDetail elements. The same functionality is added to the equivalent unchecked GatewayRuntimeException. The two exceptions are intended to behave identically, with the unchecked variant to be used only where a checked exception cannot be thrown due to the standard APIs exposed. For example, the Iterator methods used when retrieving events. Signed-off-by: Mark S. Lewis <[email protected]>
- Loading branch information
1 parent
be53d26
commit 8993d6d
Showing
7 changed files
with
208 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
java/src/main/java/org/hyperledger/fabric/client/GrpcStackTracePrinter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* Copyright 2021 IBM All Rights Reserved. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.hyperledger.fabric.client; | ||
|
||
import java.io.CharArrayWriter; | ||
import java.io.PrintStream; | ||
import java.io.PrintWriter; | ||
import java.util.List; | ||
import java.util.function.Consumer; | ||
import org.hyperledger.fabric.protos.gateway.ErrorDetail; | ||
|
||
class GrpcStackTracePrinter { | ||
private final Consumer<PrintWriter> printStackTraceFn; | ||
private final GrpcStatus grpcStatus; | ||
|
||
public GrpcStackTracePrinter(final Consumer<PrintWriter> printStackTraceFn, final GrpcStatus grpcStatus) { | ||
this.printStackTraceFn = printStackTraceFn; | ||
this.grpcStatus = grpcStatus; | ||
} | ||
|
||
public void printStackTrace(final PrintStream out) { | ||
PrintWriter writer = new PrintWriter(out); | ||
printStackTrace(writer); | ||
writer.flush(); | ||
} | ||
|
||
public void printStackTrace(final PrintWriter out) { | ||
CharArrayWriter message = new CharArrayWriter(); | ||
|
||
try (PrintWriter printer = new PrintWriter(message)) { | ||
printStackTraceFn.accept(printer); | ||
} | ||
|
||
List<ErrorDetail> details = grpcStatus.getDetails(); | ||
if (!details.isEmpty()) { | ||
message.append("Error details:\n"); | ||
for (ErrorDetail detail : details) { | ||
message.append(" address: ") | ||
.append(detail.getAddress()) | ||
.append("; mspId: ") | ||
.append(detail.getMspId()) | ||
.append("; message: ") | ||
.append(detail.getMessage()) | ||
.append('\n'); | ||
} | ||
} | ||
|
||
out.print(message); | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
java/src/test/java/org/hyperledger/fabric/client/CommonGatewayExceptionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright 2024 IBM All Rights Reserved. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.hyperledger.fabric.client; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import com.google.protobuf.Any; | ||
import com.google.rpc.Code; | ||
import io.grpc.StatusRuntimeException; | ||
import io.grpc.protobuf.StatusProto; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.PrintStream; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
import org.hyperledger.fabric.protos.gateway.ErrorDetail; | ||
import org.junit.jupiter.api.Test; | ||
|
||
abstract class CommonGatewayExceptionTest { | ||
protected abstract Exception newInstance(StatusRuntimeException e); | ||
|
||
@Test | ||
void error_details_are_printed() { | ||
List<ErrorDetail> details = Arrays.asList( | ||
ErrorDetail.newBuilder() | ||
.setAddress("ADDRESS1") | ||
.setMspId("MSPID1") | ||
.setMessage("MESSAGE1") | ||
.build(), | ||
ErrorDetail.newBuilder() | ||
.setAddress("ADDRESS2") | ||
.setMspId("MSPID2") | ||
.setMessage("MESSAGE2") | ||
.build()); | ||
Exception e = newInstance(newStatusRuntimeException(Code.ABORTED, "STATUS_MESSAGE", details)); | ||
|
||
ByteArrayOutputStream actual = new ByteArrayOutputStream(); | ||
try (PrintStream out = new PrintStream(actual)) { | ||
e.printStackTrace(out); | ||
} | ||
|
||
List<String> expected = details.stream() | ||
.flatMap(detail -> Stream.of(detail.getAddress(), detail.getMspId(), detail.getMessage())) | ||
.collect(Collectors.toList()); | ||
assertThat(actual.toString()).contains(expected); | ||
} | ||
|
||
@Test | ||
void message_from_StatusRuntimeException_is_printed() { | ||
Exception e = newInstance(newStatusRuntimeException(Code.ABORTED, "STATUS_MESSAGE", Collections.emptyList())); | ||
|
||
ByteArrayOutputStream actual = new ByteArrayOutputStream(); | ||
try (PrintStream out = new PrintStream(actual)) { | ||
e.printStackTrace(out); | ||
} | ||
|
||
String expected = e.getCause().getLocalizedMessage(); | ||
assertThat(actual.toString()).contains(expected); | ||
} | ||
|
||
@Test | ||
void print_stream_passed_to_printStackTrace_not_closed() { | ||
Exception e = newInstance(newStatusRuntimeException(Code.ABORTED, "", Collections.emptyList())); | ||
|
||
ByteArrayOutputStream actual = new ByteArrayOutputStream(); | ||
try (PrintStream out = new PrintStream(actual)) { | ||
e.printStackTrace(out); | ||
out.println("EXPECTED_SUBSEQUENT_MESSAGE"); | ||
} | ||
|
||
assertThat(actual.toString()).contains("EXPECTED_SUBSEQUENT_MESSAGE"); | ||
} | ||
|
||
private StatusRuntimeException newStatusRuntimeException(Code code, String message, List<ErrorDetail> details) { | ||
List<Any> anyDetails = details.stream().map(Any::pack).collect(Collectors.toList()); | ||
com.google.rpc.Status status = com.google.rpc.Status.newBuilder() | ||
.setCode(code.getNumber()) | ||
.setMessage(message) | ||
.addAllDetails(anyDetails) | ||
.build(); | ||
return StatusProto.toStatusRuntimeException(status); | ||
} | ||
} |
Oops, something went wrong.