Skip to content

Commit

Permalink
#827 Remove stacktrace elements from FbExceptionBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
mrotteveel committed Dec 2, 2024
1 parent 67df11e commit 40068b8
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 5 deletions.
6 changes: 6 additions & 0 deletions src/docs/asciidoc/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ Changes per Jaybird 6 release.
See also <<whats-new-in-jaybird-6>>.
For known issues, consult <<known-issues>>.

=== Jaybird 6.0.0

The following was fixed or changed after 6.0.0-beta-1:

* Improvement: stacktraces produced by `FbExceptionBuilder` no longer include `StackTraceElements` of the builder itself (https://github.com/FirebirdSQL/jaybird/issues/827[#827])

[#jaybird-6-0-0-beta-1-changelog]
=== Jaybird 6.0.0-beta-1

Expand Down
65 changes: 60 additions & 5 deletions src/main/org/firebirdsql/gds/ng/FbExceptionBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ private static CachedMessage getCachedMessage(int errorCode) {
*/
public static SQLException ioWriteError(IOException e) {
CachedMessage error = getCachedMessage(isc_net_write_err);
return new SQLNonTransientConnectionException(error.message, error.sqlState, isc_net_write_err, e);
return stripBuilderStackTraceElements(
new SQLNonTransientConnectionException(error.message, error.sqlState, isc_net_write_err, e));
}

/**
Expand All @@ -295,7 +296,8 @@ public static SQLException ioWriteError(IOException e) {
*/
public static SQLException ioReadError(IOException e) {
CachedMessage error = getCachedMessage(isc_net_read_err);
return new SQLNonTransientConnectionException(error.message, error.sqlState, isc_net_read_err, e);
return stripBuilderStackTraceElements(
new SQLNonTransientConnectionException(error.message, error.sqlState, isc_net_read_err, e));
}

/**
Expand All @@ -306,7 +308,8 @@ public static SQLException ioReadError(IOException e) {
*/
public static SQLException connectionClosed() {
CachedMessage error = getCachedMessage(jb_connectionClosed);
return new SQLNonTransientConnectionException(error.message, error.sqlState, jb_connectionClosed);
return stripBuilderStackTraceElements(
new SQLNonTransientConnectionException(error.message, error.sqlState, jb_connectionClosed));
}

/**
Expand Down Expand Up @@ -600,7 +603,7 @@ public SQLException toFlatSQLException() {
SQLException exception = exceptionType.createSQLException(
fullExceptionMessage.toString(), interestingExceptionInfo.sqlState, interestingExceptionInfo.errorCode);
exception.initCause(chain.getException());
return exception;
return stripBuilderStackTraceElements(exception);
}

private void checkNonEmpty() {
Expand Down Expand Up @@ -718,6 +721,58 @@ private void checkExceptionInformation() throws IllegalStateException {
}
}

/**
* Removes the {@link StackTraceElement} from this builder class or its nested classes from {@code exception}.
*
* @param exception
* exception to modify
* @return same object as {@exception} after modification
* @since 6
*/
private static SQLException stripBuilderStackTraceElements(SQLException exception) {
exception.setStackTrace(stripBuilderStackTraceElements(exception.getStackTrace()));
return exception;
}

/**
* Removes the {@link StackTraceElement} from this builder class or its nested classes from
* {@code stackTraceElements}.
*
* @param stackTraceElements
* original stacktrace elements
* @return new array of {@link StackTraceElement} with the elements of this builder class or its nested classes
* removed (original array if there were no such elements, or all elements are from this builder class)
* @since 6
*/
private static StackTraceElement[] stripBuilderStackTraceElements(StackTraceElement[] stackTraceElements) {
int startIndex = findFirstNonBuilderElement(stackTraceElements);
// No elements or all elements from this class, return original.
// This is unlikely to happen in practice, unless this method is called multiple times on the same exception
if (startIndex <= 0) return stackTraceElements;
return Arrays.copyOfRange(stackTraceElements, startIndex, stackTraceElements.length);
}

/**
* Finds the first {@link StackTraceElement} that was not produced by this builder class or its nested classes.
*
* @param stackTraceElements
* stacktrace elements to search
* @return position of first element that was not produce by this builder class or its nested classes, {@code -1} if
* all elements are from this builder class
* @since 6
*/
private static int findFirstNonBuilderElement(StackTraceElement[] stackTraceElements) {
final String thisClassName = FbExceptionBuilder.class.getName();
final String nestedClassPrefix = thisClassName + "$";
for (int idx = 0; idx < stackTraceElements.length; idx++) {
String className = stackTraceElements[idx].getClassName();
if (!className.equals(thisClassName) && !className.startsWith(nestedClassPrefix)) {
return idx;
}
}
return -1;
}

private static final class ExceptionInformation {
private final Type type;
private final List<String> messageParameters = new ArrayList<>();
Expand Down Expand Up @@ -793,7 +848,7 @@ SQLException toSQLException() {
if (cause != null) {
result.initCause(cause);
}
return result;
return stripBuilderStackTraceElements(result);
}

FBSQLExceptionInfo toSQLExceptionInfo() {
Expand Down
16 changes: 16 additions & 0 deletions src/test/org/firebirdsql/gds/ng/FbExceptionBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,22 @@ void exceptionTypeUpgrade_EXCEPTION_to_NON_TRANSIENT() {
"Expected result to be SQLInvalidAuthorizationSpecException (no subclass!)");
}

@Test
void toSQLExceptionStripsBuilderStackTraceElements() {
SQLException result = FbExceptionBuilder.forException(isc_login).toSQLException();

assertEquals(getClass().getName(), result.getStackTrace()[0].getClassName(),
"Expected first StackTraceElement to be from this class");
}

@Test
void toFlatSQLExceptionStripsBuilderStackTraceElements() {
SQLException result = FbExceptionBuilder.forException(isc_login).toFlatSQLException();

assertEquals(getClass().getName(), result.getStackTrace()[0].getClassName(),
"Expected first StackTraceElement to be from this class");
}

/**
* Helper method to assert exception for an uninitialized exception type.
*/
Expand Down

0 comments on commit 40068b8

Please sign in to comment.