/* * Copyright (c) 2019-2020 "Neo4j," * Neo4j Sweden AB [https://neo4j.com] * * This file is part of Neo4j. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.neo4j.springframework.data.repository.support; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; import org.apache.commons.logging.LogFactory; import org.apiguardian.api.API; import org.neo4j.driver.exceptions.*; import org.neo4j.driver.exceptions.value.ValueException; import org.springframework.core.log.LogAccessor; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.dao.NonTransientDataAccessResourceException; import org.springframework.dao.PermissionDeniedDataAccessException; import org.springframework.dao.RecoverableDataAccessException; import org.springframework.dao.TransientDataAccessResourceException; import org.springframework.dao.support.PersistenceExceptionTranslator; /** * A PersistenceExceptionTranslator to get picked up by the Spring exception translation infrastructure. * * @author Michael J. Simons * @soundtrack Kummer - KIOX * @since 1.0 */ @API(status = API.Status.STABLE, since = "1.0") public final class Neo4jPersistenceExceptionTranslator implements PersistenceExceptionTranslator { private static final LogAccessor log = new LogAccessor( LogFactory.getLog(Neo4jPersistenceExceptionTranslator.class)); private static final Map<String, Optional<BiFunction<String, Throwable, DataAccessException>>> ERROR_CODE_MAPPINGS; @Override public DataAccessException translateExceptionIfPossible(RuntimeException ex) { if (ex instanceof DataAccessException) { return (DataAccessException) ex; } else if (ex instanceof DiscoveryException) { return translateImpl((Neo4jException) ex, TransientDataAccessResourceException::new); } else if (ex instanceof DatabaseException) { return translateImpl((Neo4jException) ex, NonTransientDataAccessResourceException::new); } else if (ex instanceof ServiceUnavailableException) { return translateImpl((Neo4jException) ex, NonTransientDataAccessResourceException::new); } else if (ex instanceof SessionExpiredException) { return translateImpl((Neo4jException) ex, RecoverableDataAccessException::new); } else if (ex instanceof ProtocolException) { return translateImpl((Neo4jException) ex, NonTransientDataAccessResourceException::new); } else if (ex instanceof TransientException) { return translateImpl((Neo4jException) ex, TransientDataAccessResourceException::new); } else if (ex instanceof ValueException) { return translateImpl((Neo4jException) ex, InvalidDataAccessApiUsageException::new); } else if (ex instanceof AuthenticationException) { return translateImpl((Neo4jException) ex, PermissionDeniedDataAccessException::new); } else if (ex instanceof ResultConsumedException) { return translateImpl((Neo4jException) ex, InvalidDataAccessApiUsageException::new); } else if (ex instanceof FatalDiscoveryException) { return translateImpl((Neo4jException) ex, NonTransientDataAccessResourceException::new); } else if (ex instanceof TransactionNestingException) { return translateImpl((Neo4jException) ex, InvalidDataAccessApiUsageException::new); } else if (ex instanceof ClientException) { return translateImpl((Neo4jException) ex, InvalidDataAccessResourceUsageException::new); } log.warn(() -> String.format("Don't know how to translate exception of type %s", ex.getClass())); return null; } private static DataAccessException translateImpl(Neo4jException e, BiFunction<String, Throwable, DataAccessException> defaultTranslationProvider) { Optional<String> optionalErrorCode = Optional.ofNullable(e.code()); String msg = String.format("%s; Error code '%s'", e.getMessage(), optionalErrorCode.orElse("n/a")); return optionalErrorCode.flatMap(code -> ERROR_CODE_MAPPINGS.getOrDefault(code, Optional.empty())) .orElse(defaultTranslationProvider).apply(msg, e.getCause()); } static { Map<String, Optional<BiFunction<String, Throwable, DataAccessException>>> tmp = new HashMap<>(); // Error codes as of Neo4j 4.0.0 // https://neo4j.com/docs/status-codes/current/ tmp.put("Neo.ClientError.Cluster.NotALeader", Optional.empty()); tmp.put("Neo.ClientError.Database.DatabaseNotFound", Optional.empty()); tmp.put("Neo.ClientError.Database.ExistingDatabaseFound", Optional.empty()); tmp.put("Neo.ClientError.Fabric.AccessMode", Optional.empty()); tmp.put("Neo.ClientError.General.ForbiddenOnReadOnlyDatabase", Optional.empty()); tmp.put("Neo.ClientError.General.InvalidArguments", Optional.empty()); tmp.put("Neo.ClientError.Procedure.ProcedureCallFailed", Optional.empty()); tmp.put("Neo.ClientError.Procedure.ProcedureNotFound", Optional.empty()); tmp.put("Neo.ClientError.Procedure.ProcedureRegistrationFailed", Optional.empty()); tmp.put("Neo.ClientError.Procedure.ProcedureTimedOut", Optional.empty()); tmp.put("Neo.ClientError.Procedure.TypeError", Optional.empty()); tmp.put("Neo.ClientError.Request.Invalid", Optional.empty()); tmp.put("Neo.ClientError.Request.InvalidFormat", Optional.empty()); tmp.put("Neo.ClientError.Request.InvalidUsage", Optional.empty()); tmp.put("Neo.ClientError.Schema.ConstraintAlreadyExists", Optional.empty()); tmp.put("Neo.ClientError.Schema.ConstraintNotFound", Optional.empty()); tmp.put("Neo.ClientError.Schema.ConstraintValidationFailed", Optional.of(DataIntegrityViolationException::new)); tmp.put("Neo.ClientError.Schema.ConstraintViolation", Optional.of(DataIntegrityViolationException::new)); tmp.put("Neo.ClientError.Schema.ConstraintWithNameAlreadyExists", Optional.empty()); tmp.put("Neo.ClientError.Schema.EquivalentSchemaRuleAlreadyExists", Optional.empty()); tmp.put("Neo.ClientError.Schema.ForbiddenOnConstraintIndex", Optional.empty()); tmp.put("Neo.ClientError.Schema.IndexAlreadyExists", Optional.empty()); tmp.put("Neo.ClientError.Schema.IndexMultipleFound", Optional.empty()); tmp.put("Neo.ClientError.Schema.IndexNotApplicable", Optional.empty()); tmp.put("Neo.ClientError.Schema.IndexNotFound", Optional.empty()); tmp.put("Neo.ClientError.Schema.IndexWithNameAlreadyExists", Optional.empty()); tmp.put("Neo.ClientError.Schema.RepeatedLabelInSchema", Optional.empty()); tmp.put("Neo.ClientError.Schema.RepeatedPropertyInCompositeSchema", Optional.empty()); tmp.put("Neo.ClientError.Schema.RepeatedRelationshipTypeInSchema", Optional.empty()); tmp.put("Neo.ClientError.Schema.TokenNameError", Optional.empty()); tmp.put("Neo.ClientError.Security.AuthenticationRateLimit", Optional.empty()); tmp.put("Neo.ClientError.Security.AuthorizationExpired", Optional.empty()); tmp.put("Neo.ClientError.Security.CredentialsExpired", Optional.empty()); tmp.put("Neo.ClientError.Security.Forbidden", Optional.empty()); tmp.put("Neo.ClientError.Security.Unauthorized", Optional.empty()); tmp.put("Neo.ClientError.Statement.ArgumentError", Optional.empty()); tmp.put("Neo.ClientError.Statement.ArithmeticError", Optional.empty()); tmp.put("Neo.ClientError.Statement.ConstraintVerificationFailed", Optional.empty()); tmp.put("Neo.ClientError.Statement.EntityNotFound", Optional.empty()); tmp.put("Neo.ClientError.Statement.ExternalResourceFailed", Optional.empty()); tmp.put("Neo.ClientError.Statement.NotSystemDatabaseError", Optional.empty()); tmp.put("Neo.ClientError.Statement.ParameterMissing", Optional.empty()); tmp.put("Neo.ClientError.Statement.PropertyNotFound", Optional.empty()); tmp.put("Neo.ClientError.Statement.RuntimeUnsupportedError", Optional.empty()); tmp.put("Neo.ClientError.Statement.SemanticError", Optional.empty()); tmp.put("Neo.ClientError.Statement.SyntaxError", Optional.empty()); tmp.put("Neo.ClientError.Statement.TypeError", Optional.empty()); tmp.put("Neo.ClientError.Transaction.ForbiddenDueToTransactionType", Optional.empty()); tmp.put("Neo.ClientError.Transaction.InvalidBookmark", Optional.empty()); tmp.put("Neo.ClientError.Transaction.InvalidBookmarkMixture", Optional.empty()); tmp.put("Neo.ClientError.Transaction.TransactionAccessedConcurrently", Optional.empty()); tmp.put("Neo.ClientError.Transaction.TransactionHookFailed", Optional.empty()); tmp.put("Neo.ClientError.Transaction.TransactionMarkedAsFailed", Optional.empty()); tmp.put("Neo.ClientError.Transaction.TransactionNotFound", Optional.empty()); tmp.put("Neo.ClientError.Transaction.TransactionTimedOut", Optional.empty()); tmp.put("Neo.ClientError.Transaction.TransactionValidationFailed", Optional.empty()); tmp.put("Neo.ClientNotification.Procedure.ProcedureWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.CartesianProductWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.DynamicPropertyWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.EagerOperatorWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.ExhaustiveShortestPathWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.ExperimentalFeature", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.FeatureDeprecationWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.JoinHintUnfulfillableWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.NoApplicableIndexWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.RuntimeUnsupportedWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.SuboptimalIndexForWildcardQuery", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.UnboundedVariableLengthPatternWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.UnknownLabelWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.UnknownPropertyKeyWarning", Optional.empty()); tmp.put("Neo.ClientNotification.Statement.UnknownRelationshipTypeWarning", Optional.empty()); tmp.put("Neo.DatabaseError.Database.DatabaseLimitReached", Optional.empty()); tmp.put("Neo.DatabaseError.Database.UnableToStartDatabase", Optional.empty()); tmp.put("Neo.DatabaseError.Database.Unknown", Optional.empty()); tmp.put("Neo.DatabaseError.Fabric.RemoteExecutionFailed", Optional.empty()); tmp.put("Neo.DatabaseError.General.IndexCorruptionDetected", Optional.empty()); tmp.put("Neo.DatabaseError.General.SchemaCorruptionDetected", Optional.empty()); tmp.put("Neo.DatabaseError.General.StorageDamageDetected", Optional.empty()); tmp.put("Neo.DatabaseError.General.UnknownError", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.ConstraintCreationFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.ConstraintDropFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.IndexCreationFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.IndexDropFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.LabelAccessFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.PropertyKeyAccessFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.RelationshipTypeAccessFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.SchemaRuleAccessFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.SchemaRuleDuplicateFound", Optional.empty()); tmp.put("Neo.DatabaseError.Schema.TokenLimitReached", Optional.empty()); tmp.put("Neo.DatabaseError.Statement.CodeGenerationFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Statement.ExecutionFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Transaction.TransactionCommitFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Transaction.TransactionLogError", Optional.empty()); tmp.put("Neo.DatabaseError.Transaction.TransactionRollbackFailed", Optional.empty()); tmp.put("Neo.DatabaseError.Transaction.TransactionStartFailed", Optional.empty()); tmp.put("Neo.TransientError.Cluster.ReplicationFailure", Optional.empty()); tmp.put("Neo.TransientError.Database.DatabaseUnavailable", Optional.empty()); tmp.put("Neo.TransientError.General.OutOfMemoryError", Optional.empty()); tmp.put("Neo.TransientError.General.StackOverFlowError", Optional.empty()); tmp.put("Neo.TransientError.General.TransactionMemoryLimit", Optional.empty()); tmp.put("Neo.TransientError.General.TransactionOutOfMemoryError", Optional.empty()); tmp.put("Neo.TransientError.Request.NoThreadsAvailable", Optional.empty()); tmp.put("Neo.TransientError.Security.AuthProviderFailed", Optional.empty()); tmp.put("Neo.TransientError.Security.AuthProviderTimeout", Optional.empty()); tmp.put("Neo.TransientError.Security.ModifiedConcurrently", Optional.empty()); tmp.put("Neo.TransientError.Transaction.BookmarkTimeout", Optional.empty()); tmp.put("Neo.TransientError.Transaction.ConstraintsChanged", Optional.empty()); tmp.put("Neo.TransientError.Transaction.DeadlockDetected", Optional.empty()); tmp.put("Neo.TransientError.Transaction.Interrupted", Optional.empty()); tmp.put("Neo.TransientError.Transaction.LeaseExpired", Optional.empty()); tmp.put("Neo.TransientError.Transaction.LockAcquisitionTimeout", Optional.empty()); tmp.put("Neo.TransientError.Transaction.LockClientStopped", Optional.empty()); tmp.put("Neo.TransientError.Transaction.MaximumTransactionLimitReached", Optional.empty()); tmp.put("Neo.TransientError.Transaction.Outdated", Optional.empty()); tmp.put("Neo.TransientError.Transaction.Terminated", Optional.empty()); ERROR_CODE_MAPPINGS = Collections.unmodifiableMap(tmp); } }