8000 [DE-682] #281 better exception translation by aburmeis · Pull Request #282 · arangodb/spring-data · GitHub
[go: up one dir, main page]

Skip to content

[DE-682] #281 better exception translation #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

8000 By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 6, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
#281 more specific exception translation
  • Loading branch information
aburmeis committed Sep 6, 2023
commit ce9fec83da45df168dd7f4bd4922a11116be6475
Original file line number Diff line number Diff line change
Expand Up @@ -20,57 +20,53 @@

package com.arangodb.springframework.core.util;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PermissionDeniedDataAccessException;
import org.springframework.dao.*;
import org.springframework.dao.support.PersistenceExceptionTranslator;

import com.arangodb.ArangoDBException;
import com.arangodb.springframework.ArangoUncategorizedException;

import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Predicate;

import static java.util.Map.entry;

/**
* Translate any {@link ArangoDBException} to the appropriate {@link DataAccessException} using response code and error number.
*
* @see <a href="https://github.com/arangodb/arangodb/blob/devel/lib/Basics/errors.dat">General ArangoDB storage errors</a>
* @author Mark Vollmary
* @author Christian Lechner
*
* @author Arne Burmeister
*/
public class ArangoExceptionTranslator implements PersistenceExceptionTranslator {

@Override
public DataAccessException translateExceptionIfPossible(final RuntimeException ex) {
DataAccessException dae = null;
if (ex instanceof DataAccessException) {
dae = DataAccessException.class.cast(ex);
} else if (ex instanceof ArangoDBException) {
final ArangoDBException exception = ArangoDBException.class.cast(ex);
if (ex instanceof DataAccessException exception) {
dae = exception;
} else if (ex instanceof ArangoDBException exception) {
final Integer responseCode = exception.getResponseCode();
if (responseCode != null) {
switch (responseCode) {
case ArangoErrors.ERROR_HTTP_UNAUTHORIZED:
case ArangoErrors.ERROR_HTTP_FORBIDDEN:
dae = new PermissionDeniedDataAccessException(exception.getMessage(), exception);
break;
case ArangoErrors.ERROR_HTTP_BAD_PARAMETER:
case ArangoErrors.ERROR_HTTP_METHOD_NOT_ALLOWED:
dae = new InvalidDataAccessApiUsageException(exception.getMessage(), exception);
break;
case ArangoErrors.ERROR_HTTP_NOT_FOUND:
dae = new InvalidDataAccessResourceUsageException(exception.getMessage(), exception);
break;
case ArangoErrors.ERROR_HTTP_CONFLICT:
dae = new DataIntegrityViolationException(exception.getMessage(), exception);
break;
case ArangoErrors.ERROR_HTTP_PRECONDITION_FAILED:
case ArangoErrors.ERROR_HTTP_SERVICE_UNAVAILABLE:
dae = new DataAccessResourceFailureException(exception.getMessage(), exception);
break;
case ArangoErrors.ERROR_HTTP_SERVER_ERROR:
default:
dae = new ArangoUncategorizedException(exception.getMessage(), exception);
break;
}
BiFunction<String, ArangoDBException, DataAccessException> constructor = switch (responseCode) {
case ArangoErrors.ERROR_HTTP_UNAUTHORIZED, ArangoErrors.ERROR_HTTP_FORBIDDEN -> PermissionDeniedDataAccessException::new;
case ArangoErrors.ERROR_HTTP_BAD_PARAMETER, ArangoErrors.ERROR_HTTP_METHOD_NOT_ALLOWED -> InvalidDataAccessApiUsageException::new;
case ArangoErrors.ERROR_HTTP_NOT_FOUND -> mostSpecific(exception, Map.ofEntries(
entry(hasErrorNumber(1202), DataRetrievalFailureException::new)
), InvalidDataAccessResourceUsageException::new);
case ArangoErrors.ERROR_HTTP_CONFLICT -> mostSpecific(exception, Map.ofEntries(
entry(hasErrorNumber(1200).and(errorMessageContains("write-write conflict")), TransientDataAccessResourceException::new),
entry(hasErrorNumber(1200).and(errorMessageContains("_rev")), OptimisticLockingFailureException::new),
entry(hasErrorNumber(1210).and(errorMessageContains("_key")), DuplicateKeyException::new)
),
DataIntegrityViolationException::new);
case ArangoErrors.ERROR_HTTP_PRECONDITION_FAILED -> OptimisticLockingFailureException::new;
case ArangoErrors.ERROR_HTTP_SERVICE_UNAVAILABLE -> DataAccessResourceFailureException::new;
default -> ArangoUncategorizedException::new;
};
return constructor.apply(exception.getMessage(), exception);
}
}
if (dae == null) {
Expand All @@ -79,4 +75,21 @@ public DataAccessException translateExceptionIfPossible(final RuntimeException e
return dae;
}

private static BiFunction<String, ArangoDBException, DataAccessException> mostSpecific(ArangoDBException exception,
Map<Predicate<ArangoDBException>, BiFunction<String, ArangoDBException, DataAccessException>> specific,
BiFunction<String, ArangoDBException, DataAccessException> fallback) {
return specific.entrySet().stream()
.filter(entry -> entry.getKey().test(exception))
.map(Map.Entry::getValue)
.findAny()
.orElse(fallback);
}

private static Predicate<ArangoDBException> hasErrorNumber(int expected) {
return exception -> exception.getErrorNum() != null && exception.getErrorNum() == expected;
}

private static Predicate<ArangoDBException> errorMessageContains(String expected) {
return exception -> exception.getErrorMessage() != null && exception.getErrorMessage().contains(expected);
}
}
0