I'm working on a Quarkus application using Mutiny for reactive programming, and I'm encountering an issue with exception handling in my service layer. I have a method that persists a user, and I want to throw specific exceptions based on the type of error that occurs. However, I'm running into the following challenges:
Here's the relevant code snippet:
public Uni<AuthResponse> persistUser(User user) {
return Panache.withTransaction(user::persist)
.onItem().transform(success -> {
log.info("Successfully created user, email: {}", user.getEmail());
return new AuthResponse(user);
})
.onFailure(ConstraintViolationException.class)
.transform(ex -> handleUserCreationError(user.getEmail()))
.onFailure()
.transform(ex -> handleGeneralPersistenceError(ex, user.getEmail()));
}
private Throwable handleUserCreationError(String email) {
log.error("User already exists, email: {}", email);
return new AccountAlreadyExistsException(ExceptionMessages.ACCOUNT_ALREADY_EXISTS + email);
}
private Throwable handleGeneralPersistenceError(Throwable ex, String email) {
log.error("Error occurred while creating user: {}, exception: {}",email, ex.getMessage());
return new PersistenceException("Failed to create user", ex);
}
Issues: Multiple Exceptions: Without using .transform(), multiple exceptions are thrown when a ConstraintViolationException occurs. Both Transforms Triggered: When I use .transform(), both transform blocks are triggered, which is not the desired behavior.
Desired Behavior: I want to ensure that when a ConstraintViolationException occurs, only handleUserCreationError is called, and it does not proceed to call handleGeneralPersistenceError. Additionally, I want to throw the appropriate exceptions based on the type of error without causing multiple exceptions.
How can I achieve this? Any help or insights on how to correctly handle exceptions in this scenario would be greatly appreciated!
handleGeneralPersistenceError
is being called because this is an exception handler that is not tied to any specific type, so this has to be expected because the previous onFailure(Class).transform(...)
maps an exception to another one, and it gets forwarded in the pipeline to onFailure().transform(...)
.
I suggest that you simply condense the logic into a single transformation of the form:
onFailure().transform(err -> {
if (err instanceof X) {
// ...
} else if (err instanceof Y) {
// ...
}
})