Skip to content

Commit

Permalink
Extract transiency check to Exception utils and other review remarks
Browse files Browse the repository at this point in the history
  • Loading branch information
sandjelkovic committed May 11, 2021
1 parent 5e06684 commit 118eb8b
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.axonframework.commandhandling.CommandExecutionException;
import org.axonframework.commandhandling.NoHandlerForCommandException;
import org.axonframework.common.AxonException;
import org.axonframework.common.ExceptionUtils;
import org.axonframework.eventsourcing.eventstore.EventStoreException;
import org.axonframework.messaging.EventPublicationFailedException;
import org.axonframework.messaging.HandlerExecutionException;
Expand Down Expand Up @@ -238,7 +239,7 @@ public AxonException convert(String source, Throwable throwable) {
* @return {@link ErrorCode} variation
*/
public static ErrorCode getQueryExecutionErrorCode(Throwable throwable) {
if (ExceptionSerializer.isExplicitlyNonTransient(throwable)) {
if (ExceptionUtils.isExplicitlyNonTransient(throwable)) {
return ErrorCode.QUERY_EXECUTION_NON_TRANSIENT_ERROR;
} else {
return ErrorCode.QUERY_EXECUTION_ERROR;
Expand All @@ -251,7 +252,7 @@ public static ErrorCode getQueryExecutionErrorCode(Throwable throwable) {
* @return {@link ErrorCode} variation
*/
public static ErrorCode getCommandExecutionErrorCode(Throwable throwable) {
if (ExceptionSerializer.isExplicitlyNonTransient(throwable)) {
if (ExceptionUtils.isExplicitlyNonTransient(throwable)) {
return ErrorCode.COMMAND_EXECUTION_NON_TRANSIENT_ERROR;
} else {
return ErrorCode.COMMAND_EXECUTION_ERROR;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*/
public class AxonServerNonTransientRemoteCommandHandlingException extends RemoteNonTransientHandlingException {

public static final boolean PERSISTENT = true;
private static final boolean PERSISTENT = true;
private final String errorCode;
private final String server;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*/
public class AxonServerNonTransientRemoteQueryHandlingException extends RemoteNonTransientHandlingException {

public static final boolean PERSISTENT = true;
private static final boolean PERSISTENT = true;
private final String errorCode;
private final String server;

Expand All @@ -38,10 +38,10 @@ public class AxonServerNonTransientRemoteQueryHandlingException extends RemoteNo
* @param errorCode the code reported by the server
* @param errorMessage the message describing the exception on the remote end
*/
public AxonServerNonTransientRemoteQueryHandlingException(String errorCode, ErrorMessage message) {
super(new RemoteExceptionDescription(message.getDetailsList(), PERSISTENT));
public AxonServerNonTransientRemoteQueryHandlingException(String errorCode, ErrorMessage errorMessage) {
super(new RemoteExceptionDescription(errorMessage.getDetailsList(), PERSISTENT));
this.errorCode = errorCode;
this.server = message.getLocation();
this.server = errorMessage.getLocation();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.axonframework.axonserver.connector.util;

import io.axoniq.axonserver.grpc.ErrorMessage;
import org.axonframework.common.AxonNonTransientException;

import static org.axonframework.common.ObjectUtils.getOrDefault;

Expand Down Expand Up @@ -54,15 +53,4 @@ public static ErrorMessage serialize(String clientLocation, Throwable t) {
return builder.build();
}

/**
* Indicates whether the given {@code failure} is clearly non-transient. Non-transient exceptions indicate
* that the handling of the message will fail in the same way if retried
*
* @param failure the exception that occurred while processing a message
* @return {@code true} if the exception is clearly non-transient
*/
public static boolean isExplicitlyNonTransient(Throwable failure) {
return failure instanceof AxonNonTransientException
|| (failure.getCause() != null && isExplicitlyNonTransient(failure.getCause()));
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.axonframework.axonserver.connector.util;

import io.axoniq.axonserver.grpc.ErrorMessage;
import org.axonframework.commandhandling.NoHandlerForCommandException;
import org.axonframework.serialization.SerializationException;
import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.*;
Expand All @@ -26,31 +24,4 @@ void serializeNonNullClient() {
"Something went wrong"));
assertEquals("Client", result.getLocation());
}

@Test
void isExplicitlyNonTransientForTransientExceptions() {
NoHandlerForCommandException transientException = new NoHandlerForCommandException("No handler message");
assertFalse(ExceptionSerializer.isExplicitlyNonTransient(transientException));
}

@Test
void isExplicitlyNonTransientForRegularExceptions() {
RuntimeException runtimeException = new RuntimeException("Something went wrong");
assertFalse(ExceptionSerializer.isExplicitlyNonTransient(runtimeException));
}

@Test
void isExplicitlyNonTransientForNonTransientExceptions() {
SerializationException nonTransientException = new SerializationException("Serialization error");
assertTrue(ExceptionSerializer.isExplicitlyNonTransient(nonTransientException));
}

@Test
void isExplicitlyNonTransientForNestedNonTransientException() {
SerializationException nonTransientException = new SerializationException("Serialization error");
RuntimeException nestedRuntimeException = new RuntimeException("Something went wrong nested", nonTransientException);
RuntimeException baseRuntimeException = new RuntimeException("Something went wrong", nestedRuntimeException);

assertTrue(ExceptionSerializer.isExplicitlyNonTransient(baseRuntimeException));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import org.axonframework.commandhandling.CommandMessage;
import org.axonframework.common.AxonConfigurationException;
import org.axonframework.common.AxonNonTransientException;
import org.axonframework.common.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -58,20 +58,6 @@ protected AbstractRetryScheduler(Builder builder) {
this.maxRetryCount = builder.maxRetryCount;
}

/**
* Indicates whether the given {@code failure} is clearly non-transient. That means, whether the
* {@code failure} explicitly states that a retry of the same Command would result in the same failure to
* occur again.
*
* @param failure the exception that occurred while processing a command
* @return {@code true} if the exception is clearly non-transient and the command should <em>not</em> be
* retried, or {@code false} when the command has a chance of succeeding if it retried.
*/
protected boolean isExplicitlyNonTransient(Throwable failure) {
return failure instanceof AxonNonTransientException
|| (failure.getCause() != null && isExplicitlyNonTransient(failure.getCause()));
}

/**
* Schedule the provided task to run after the given interval.
*
Expand Down Expand Up @@ -117,7 +103,7 @@ public boolean scheduleRetry(CommandMessage commandMessage,
List<Class<? extends Throwable>[]> failures,
Runnable dispatchTask) {
int failureCount = failures.size();
if (!isExplicitlyNonTransient(lastFailure) && failureCount <= maxRetryCount) {
if (!ExceptionUtils.isExplicitlyNonTransient(lastFailure) && failureCount <= maxRetryCount) {
if (logger.isInfoEnabled()) {
logger.info("Processing of Command [{}] resulted in an exception. Will retry {} more time(s)... "
+ "Exception was {}, {}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,16 @@ public static Optional<Throwable> findException(Throwable exception, Predicate<T
public static <T extends Throwable> Optional<T> findException(Throwable exception, Class<T> exceptionClass) {
return findException(exception, exceptionClass::isInstance).map(e -> (T) e);
}

/**
* Indicates whether the given {@code failure} is clearly non-transient. Non-transient exceptions indicate
* that the handling of the message will fail in the same way if retried
*
* @param failure the exception that occurred while processing a message
* @return {@code true} if the exception is clearly non-transient
*/
public static boolean isExplicitlyNonTransient(Throwable failure) {
return failure instanceof AxonNonTransientException
|| (failure.getCause() != null && isExplicitlyNonTransient(failure.getCause()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.axonframework.common.AxonNonTransientException;
import org.axonframework.common.ExceptionUtils;

import java.beans.ConstructorProperties;
import java.io.Serializable;
Expand Down Expand Up @@ -48,15 +48,10 @@ public class RemoteExceptionDescription implements Serializable {
* @return a {@link List} of {@link String} describing the given {@link Exception}
*/
public static RemoteExceptionDescription describing(Throwable exception) {
final boolean isPersistent = isExplicitlyNonTransient(exception);
final boolean isPersistent = ExceptionUtils.isExplicitlyNonTransient(exception);
return new RemoteExceptionDescription(createDescription(exception, new ArrayList<>()), isPersistent);
}

private static boolean isExplicitlyNonTransient(Throwable exception) {
return exception instanceof AxonNonTransientException
|| (exception.getCause() != null && isExplicitlyNonTransient(exception.getCause()));
}

private static List<String> createDescription(Throwable exception, List<String> descriptions) {
descriptions.add(exception.getClass().getName() + DELIMITER + exception.getMessage());
Throwable cause = exception.getCause();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.axonframework.common;

import org.axonframework.commandhandling.NoHandlerForCommandException;
import org.axonframework.serialization.SerializationException;
import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.*;

/**
* @author Stefan Andjelkovic
*/
class ExceptionUtilsTest {
@Test
void isExplicitlyNonTransientForTransientExceptions() {
NoHandlerForCommandException transientException = new NoHandlerForCommandException("No handler message");
assertFalse(ExceptionUtils.isExplicitlyNonTransient(transientException));
}

@Test
void isExplicitlyNonTransientForRegularExceptions() {
RuntimeException runtimeException = new RuntimeException("Something went wrong");
assertFalse(ExceptionUtils.isExplicitlyNonTransient(runtimeException));
}

@Test
void isExplicitlyNonTransientForNonTransientExceptions() {
SerializationException nonTransientException = new SerializationException("Serialization error");
assertTrue(ExceptionUtils.isExplicitlyNonTransient(nonTransientException));
}

@Test
void isExplicitlyNonTransientForNestedNonTransientException() {
SerializationException nonTransientException = new SerializationException("Serialization error");
RuntimeException nestedRuntimeException = new RuntimeException("Something went wrong nested", nonTransientException);
RuntimeException baseRuntimeException = new RuntimeException("Something went wrong", nestedRuntimeException);

assertTrue(ExceptionUtils.isExplicitlyNonTransient(baseRuntimeException));
}
}

0 comments on commit 118eb8b

Please sign in to comment.