DATAMONGO-2045 - Add session & transaction specific error codes for exception translation.
Original pull request: #592.
This commit is contained in:
committed by
Mark Paluch
parent
da63788a52
commit
c2373d05fe
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.springframework.data.mongodb;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A specific {@link ClientSessionException} related to issues with a transaction such as aborted or non existing
|
||||
* transactions.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
public class MongoTransactionException extends ClientSessionException {
|
||||
|
||||
/**
|
||||
* Constructor for {@link MongoTransactionException}.
|
||||
*
|
||||
* @param msg the detail message. Must not be {@literal null}.
|
||||
*/
|
||||
public MongoTransactionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for {@link ClientSessionException}.
|
||||
*
|
||||
* @param msg the detail message. Can be {@literal null}.
|
||||
* @param cause the root cause. Can be {@literal null}.
|
||||
*/
|
||||
public MongoTransactionException(@Nullable String msg, @Nullable Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.BulkOperationException;
|
||||
import org.springframework.data.mongodb.ClientSessionException;
|
||||
import org.springframework.data.mongodb.MongoTransactionException;
|
||||
import org.springframework.data.mongodb.UncategorizedMongoDbException;
|
||||
import org.springframework.data.mongodb.util.MongoDbErrorCodes;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -128,6 +129,10 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
} else if (MongoDbErrorCodes.isPermissionDeniedCode(code)) {
|
||||
return new PermissionDeniedDataAccessException(ex.getMessage(), ex);
|
||||
} else if (MongoDbErrorCodes.isClientSessionFailureCode(code)) {
|
||||
return new ClientSessionException(ex.getMessage(), ex);
|
||||
} else if (MongoDbErrorCodes.isTransactionFailureCode(code)) {
|
||||
return new MongoTransactionException(ex.getMessage(), ex);
|
||||
}
|
||||
return new UncategorizedMongoDbException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
@@ -33,12 +33,14 @@ public final class MongoDbErrorCodes {
|
||||
static HashMap<Integer, String> duplicateKeyCodes;
|
||||
static HashMap<Integer, String> invalidDataAccessApiUsageExeption;
|
||||
static HashMap<Integer, String> permissionDeniedCodes;
|
||||
static HashMap<Integer, String> clientSessionCodes;
|
||||
static HashMap<Integer, String> transactionCodes;
|
||||
|
||||
static HashMap<Integer, String> errorCodes;
|
||||
|
||||
static {
|
||||
|
||||
dataAccessResourceFailureCodes = new HashMap<Integer, String>(10);
|
||||
dataAccessResourceFailureCodes = new HashMap<>(10);
|
||||
dataAccessResourceFailureCodes.put(6, "HostUnreachable");
|
||||
dataAccessResourceFailureCodes.put(7, "HostNotFound");
|
||||
dataAccessResourceFailureCodes.put(89, "NetworkTimeout");
|
||||
@@ -52,7 +54,7 @@ public final class MongoDbErrorCodes {
|
||||
dataAccessResourceFailureCodes.put(13441, "BadOffsetInFile");
|
||||
dataAccessResourceFailureCodes.put(13640, "DataFileHeaderCorrupt");
|
||||
|
||||
dataIntegrityViolationCodes = new HashMap<Integer, String>(6);
|
||||
dataIntegrityViolationCodes = new HashMap<>(6);
|
||||
dataIntegrityViolationCodes.put(67, "CannotCreateIndex");
|
||||
dataIntegrityViolationCodes.put(68, "IndexAlreadyExists");
|
||||
dataIntegrityViolationCodes.put(85, "IndexOptionsConflict");
|
||||
@@ -60,13 +62,13 @@ public final class MongoDbErrorCodes {
|
||||
dataIntegrityViolationCodes.put(112, "WriteConflict");
|
||||
dataIntegrityViolationCodes.put(117, "ConflictingOperationInProgress");
|
||||
|
||||
duplicateKeyCodes = new HashMap<Integer, String>(3);
|
||||
duplicateKeyCodes = new HashMap<>(3);
|
||||
duplicateKeyCodes.put(3, "OBSOLETE_DuplicateKey");
|
||||
duplicateKeyCodes.put(84, "DuplicateKeyValue");
|
||||
duplicateKeyCodes.put(11000, "DuplicateKey");
|
||||
duplicateKeyCodes.put(11001, "DuplicateKey");
|
||||
|
||||
invalidDataAccessApiUsageExeption = new HashMap<Integer, String>();
|
||||
invalidDataAccessApiUsageExeption = new HashMap<>();
|
||||
invalidDataAccessApiUsageExeption.put(5, "GraphContainsCycle");
|
||||
invalidDataAccessApiUsageExeption.put(9, "FailedToParse");
|
||||
invalidDataAccessApiUsageExeption.put(14, "TypeMismatch");
|
||||
@@ -99,7 +101,7 @@ public final class MongoDbErrorCodes {
|
||||
invalidDataAccessApiUsageExeption.put(17280, "KeyTooLong");
|
||||
invalidDataAccessApiUsageExeption.put(13334, "ShardKeyTooBig");
|
||||
|
||||
permissionDeniedCodes = new HashMap<Integer, String>();
|
||||
permissionDeniedCodes = new HashMap<>();
|
||||
permissionDeniedCodes.put(11, "UserNotFound");
|
||||
permissionDeniedCodes.put(18, "AuthenticationFailed");
|
||||
permissionDeniedCodes.put(31, "RoleNotFound");
|
||||
@@ -109,12 +111,29 @@ public final class MongoDbErrorCodes {
|
||||
permissionDeniedCodes.put(16704, "CannotAuthenticateToAdminDB");
|
||||
permissionDeniedCodes.put(16705, "CannotAuthenticateToAdminDB");
|
||||
|
||||
errorCodes = new HashMap<Integer, String>();
|
||||
clientSessionCodes = new HashMap<>();
|
||||
clientSessionCodes.put(206, "NoSuchSession");
|
||||
clientSessionCodes.put(213, "DuplicateSession");
|
||||
clientSessionCodes.put(228, "SessionTransferIncomplete");
|
||||
clientSessionCodes.put(264, "TooManyLogicalSessions");
|
||||
|
||||
transactionCodes = new HashMap<>();
|
||||
transactionCodes.put(217, "IncompleteTransactionHistory");
|
||||
transactionCodes.put(225, "TransactionTooOld");
|
||||
transactionCodes.put(244, "TransactionAborted");
|
||||
transactionCodes.put(251, "NoSuchTransaction");
|
||||
transactionCodes.put(256, "TransactionCommitted");
|
||||
transactionCodes.put(257, "TransactionToLarge");
|
||||
transactionCodes.put(263, "OperationNotSupportedInTransaction");
|
||||
transactionCodes.put(267, "PreparedTransactionInProgress");
|
||||
|
||||
errorCodes = new HashMap<>();
|
||||
errorCodes.putAll(dataAccessResourceFailureCodes);
|
||||
errorCodes.putAll(dataIntegrityViolationCodes);
|
||||
errorCodes.putAll(duplicateKeyCodes);
|
||||
errorCodes.putAll(invalidDataAccessApiUsageExeption);
|
||||
errorCodes.putAll(permissionDeniedCodes);
|
||||
errorCodes.putAll(clientSessionCodes);
|
||||
}
|
||||
|
||||
public static boolean isDataIntegrityViolationCode(@Nullable Integer errorCode) {
|
||||
@@ -140,4 +159,26 @@ public final class MongoDbErrorCodes {
|
||||
public static String getErrorDescription(@Nullable Integer errorCode) {
|
||||
return errorCode == null ? null : errorCodes.get(errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given error code matches a know session related error.
|
||||
*
|
||||
* @param errorCode the error code to check.
|
||||
* @return {@literal true} if error matches.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static boolean isClientSessionFailureCode(@Nullable Integer errorCode) {
|
||||
return errorCode == null ? null : clientSessionCodes.containsKey(errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given error code matches a know transaction related error.
|
||||
*
|
||||
* @param errorCode the error code to check.
|
||||
* @return {@literal true} if error matches.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static boolean isTransactionFailureCode(@Nullable Integer errorCode) {
|
||||
return errorCode == null ? null : transactionCodes.containsKey(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,25 +17,20 @@ package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import com.mongodb.WriteConcern;
|
||||
import org.bson.BsonDocument;
|
||||
import org.bson.Document;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.mongodb.ClientSessionException;
|
||||
import org.springframework.data.mongodb.MongoTransactionException;
|
||||
import org.springframework.data.mongodb.UncategorizedMongoDbException;
|
||||
|
||||
import com.mongodb.MongoCursorNotFoundException;
|
||||
@@ -135,6 +130,28 @@ public class MongoExceptionTranslatorUnitTests {
|
||||
assertThat(translator.translateExceptionIfPossible(exception), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2045
|
||||
public void translateSessionExceptions() {
|
||||
|
||||
checkTranslatedMongoException(ClientSessionException.class, 206);
|
||||
checkTranslatedMongoException(ClientSessionException.class, 213);
|
||||
checkTranslatedMongoException(ClientSessionException.class, 228);
|
||||
checkTranslatedMongoException(ClientSessionException.class, 264);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2045
|
||||
public void translateTransactionExceptions() {
|
||||
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 217);
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 225);
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 244);
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 251);
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 256);
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 257);
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 263);
|
||||
checkTranslatedMongoException(MongoTransactionException.class, 267);
|
||||
}
|
||||
|
||||
private void checkTranslatedMongoException(Class<? extends Exception> clazz, int code) {
|
||||
|
||||
DataAccessException translated = translator.translateExceptionIfPossible(new MongoException(code, ""));
|
||||
|
||||
Reference in New Issue
Block a user