From 03ac834299d25b0290bb2d6261c12cc6caeaf7b5 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 28 Aug 2018 10:27:44 +0200 Subject: [PATCH 1/3] DATAMONGO-2073 - Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-benchmarks/pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 114420f66b..f666e1c306 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 2.1.0.BUILD-SNAPSHOT + 2.1.0.DATAMONGO-2073-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 9baccaa905..1d318ea6eb 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 2.1.0.BUILD-SNAPSHOT + 2.1.0.DATAMONGO-2073-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index 47a5b7aba7..f38bb28c0f 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 2.1.0.BUILD-SNAPSHOT + 2.1.0.DATAMONGO-2073-SNAPSHOT ../pom.xml @@ -50,7 +50,7 @@ org.springframework.data spring-data-mongodb - 2.1.0.BUILD-SNAPSHOT + 2.1.0.DATAMONGO-2073-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index e5c865ea08..a7df819f29 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 2.1.0.BUILD-SNAPSHOT + 2.1.0.DATAMONGO-2073-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index b86dc2808c..d8a0b253f3 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 2.1.0.BUILD-SNAPSHOT + 2.1.0.DATAMONGO-2073-SNAPSHOT ../pom.xml From b94c3ca35b54a1dda62e66ab78c4fdc60ba55f83 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 28 Aug 2018 13:06:00 +0200 Subject: [PATCH 2/3] DATAMONGO-2073 - Evaluate exception label when translating MongoExceptions. We now distinguish between Transient and NonTransient failures by checking the Error labels of an Error and create the according DataAccessException based on that information. --- .../TransientClientSessionException.java | 48 +++++++++++++++++ .../mongodb/TransientMongoDbException.java | 49 ++++++++++++++++++ .../TransientMongoDbTransactionException.java | 46 +++++++++++++++++ .../core/MongoExceptionTranslator.java | 51 +++++++++++++++++-- .../MongoExceptionTranslatorUnitTests.java | 40 +++++++++++++++ 5 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbTransactionException.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java new file mode 100644 index 0000000000..652f92283e --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientClientSessionException.java @@ -0,0 +1,48 @@ +/* + * 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.dao.TransientDataAccessException; +import org.springframework.lang.Nullable; + +/** + * {@link TransientDataAccessException} specific to MongoDB {@link com.mongodb.session.ClientSession} related data + * access failures such as reading data using an already closed session. + * + * @author Christoph Strobl + * @since 2.1 + */ +public class TransientClientSessionException extends TransientMongoDbException { + + /** + * Constructor for {@link TransientClientSessionException}. + * + * @param msg the detail message. Must not be {@literal null}. + */ + public TransientClientSessionException(String msg) { + super(msg); + } + + /** + * Constructor for {@link TransientClientSessionException}. + * + * @param msg the detail message. Can be {@literal null}. + * @param cause the root cause. Can be {@literal null}. + */ + public TransientClientSessionException(@Nullable String msg, @Nullable Throwable cause) { + super(msg, cause); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java new file mode 100644 index 0000000000..25bd3b501b --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbException.java @@ -0,0 +1,49 @@ +/* + * 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.dao.TransientDataAccessException; +import org.springframework.lang.Nullable; + +/** + * Root of the hierarchy of MongoDB specific data access exceptions that are considered transient such as + * {@link com.mongodb.MongoException MongoExceptions} carrying {@link com.mongodb.MongoException#hasErrorLabel(String) + * specific labels}. + * + * @author Christoph Strobl + * @since 2.1 + */ +public class TransientMongoDbException extends TransientDataAccessException { + + /** + * Constructor for {@link TransientMongoDbException}. + * + * @param msg the detail message. Must not be {@literal null}. + */ + public TransientMongoDbException(String msg) { + super(msg); + } + + /** + * Constructor for {@link TransientMongoDbException}. + * + * @param msg the detail message. Can be {@literal null}. + * @param cause the root cause. Can be {@literal null}. + */ + public TransientMongoDbException(String msg, @Nullable Throwable cause) { + super(msg, cause); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbTransactionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbTransactionException.java new file mode 100644 index 0000000000..bc26cca868 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransientMongoDbTransactionException.java @@ -0,0 +1,46 @@ +/* + * 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 TransientClientSessionException} related to issues with a transaction such as fails on commit. + * + * @author Christoph Strobl + * @since 2.1 + */ +public class TransientMongoDbTransactionException extends TransientClientSessionException { + + /** + * Constructor for {@link TransientMongoDbTransactionException}. + * + * @param msg the detail message. Must not be {@literal null}. + */ + public TransientMongoDbTransactionException(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 TransientMongoDbTransactionException(@Nullable String msg, @Nullable Throwable cause) { + super(msg, cause); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java index e3f985dab0..84021c737a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java @@ -28,10 +28,14 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.dao.PermissionDeniedDataAccessException; +import org.springframework.dao.TransientDataAccessException; 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.TransientClientSessionException; +import org.springframework.data.mongodb.TransientMongoDbException; +import org.springframework.data.mongodb.TransientMongoDbTransactionException; import org.springframework.data.mongodb.UncategorizedMongoDbException; import org.springframework.data.mongodb.util.MongoDbErrorCodes; import org.springframework.lang.Nullable; @@ -74,6 +78,21 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator @Nullable public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + DataAccessException translatedException = doTranslateException(ex); + if (translatedException == null) { + return null; + } + + // Translated exceptions that per se are not be recoverable (eg. WriteConflicts), might still be transient inside a + // transaction. Let's wrap those. + return (isTransientFailure(ex) && !(translatedException instanceof TransientDataAccessException)) + ? new TransientMongoDbException(ex.getMessage(), translatedException) : translatedException; + + } + + @Nullable + DataAccessException doTranslateException(RuntimeException ex) { + // Check for well-known MongoException subclasses. if (ex instanceof BsonInvalidOperationException) { @@ -119,7 +138,9 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { // All other MongoExceptions if (ex instanceof MongoException) { - int code = ((MongoException) ex).getCode(); + MongoException mongoException = (MongoException) ex; + int code = mongoException.getCode(); + boolean isTransient = isTransientFailure(mongoException); if (MongoDbErrorCodes.isDuplicateKeyCode(code)) { return new DuplicateKeyException(ex.getMessage(), ex); @@ -131,10 +152,13 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { } else if (MongoDbErrorCodes.isPermissionDeniedCode(code)) { return new PermissionDeniedDataAccessException(ex.getMessage(), ex); } else if (MongoDbErrorCodes.isClientSessionFailureCode(code)) { - return new ClientSessionException(ex.getMessage(), ex); + return isTransient ? new TransientClientSessionException(ex.getMessage(), ex) + : new ClientSessionException(ex.getMessage(), ex); } else if (MongoDbErrorCodes.isTransactionFailureCode(code)) { - return new MongoTransactionException(ex.getMessage(), ex); + return isTransient ? new TransientMongoDbTransactionException(ex.getMessage(), ex) + : new MongoTransactionException(ex.getMessage(), ex); } + return new UncategorizedMongoDbException(ex.getMessage(), ex); } @@ -153,4 +177,25 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { // that translation should not occur. return null; } + + /** + * Check if a given exception holds an error label indicating a transient failure. + * + * @param e + * @return {@literal true} if the given {@link Exception} is a {@link MongoException} holding one of the transient + * exception error labels. + * @see MongoException#hasErrorLabel(String) + * @since 2.1 + */ + public static boolean isTransientFailure(Exception e) { + + if (!(e instanceof MongoException)) { + return false; + } + + MongoException mongoException = (MongoException) e; + + return mongoException.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL) + || mongoException.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java index b86e793a49..039089ae31 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java @@ -18,19 +18,27 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.beans.Transient; import java.net.UnknownHostException; +import com.mongodb.MongoCommandException; +import com.mongodb.MongoWriteException; +import com.mongodb.WriteError; import org.bson.BsonDocument; import org.junit.Before; import org.junit.Test; import org.springframework.core.NestedRuntimeException; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; +import org.springframework.dao.TransientDataAccessException; import org.springframework.data.mongodb.ClientSessionException; import org.springframework.data.mongodb.MongoTransactionException; +import org.springframework.data.mongodb.TransientMongoDbException; +import org.springframework.data.mongodb.TransientMongoDbTransactionException; import org.springframework.data.mongodb.UncategorizedMongoDbException; import com.mongodb.MongoCursorNotFoundException; @@ -152,6 +160,38 @@ public void translateTransactionExceptions() { checkTranslatedMongoException(MongoTransactionException.class, 267); } + @Test // DATAMONGO-2073 + public void translateTransientTransactionExceptions() { + + MongoException source = new MongoException(267, "PreparedTransactionInProgress"); + source.addLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL); + + expectExceptionWithCauseMessage(translator.translateExceptionIfPossible(source), + TransientMongoDbTransactionException.class, "PreparedTransactionInProgress"); + } + + @Test // DATAMONGO-2073 + public void translateMongoExceptionWithTransientLabelToTransientMongoDbException() { + + MongoException exception = new MongoException(0, ""); + exception.addLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL); + DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + + expectExceptionWithCauseMessage(translatedException, TransientMongoDbException.class); + } + + @Test // DATAMONGO-2073 + public void wrapsTranslatedExceptionsWhenTransientLabelPresent() { + + MongoException exception = new MongoWriteException(new WriteError(112, "WriteConflict", new BsonDocument()), null); + exception.addLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL); + + DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + + assertThat(translatedException, is(instanceOf(TransientMongoDbException.class))); + assertThat(translatedException.getCause(), is(instanceOf(DataIntegrityViolationException.class))); + } + private void checkTranslatedMongoException(Class clazz, int code) { DataAccessException translated = translator.translateExceptionIfPossible(new MongoException(code, "")); From 3a6809b83a9c265457c4d78715c61ef6b42c494a Mon Sep 17 00:00:00 2001 From: Spring Operator Date: Sat, 16 Mar 2019 11:26:49 -0500 Subject: [PATCH 3/3] URL Cleanup This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener). # Fixed URLs ## Fixed Success These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended. * http://maven.apache.org/xsd/maven-4.0.0.xsd with 3 occurrences migrated to: https://maven.apache.org/xsd/maven-4.0.0.xsd ([https](https://maven.apache.org/xsd/maven-4.0.0.xsd) result 200). * http://www.gopivotal.com (302) with 6 occurrences migrated to: https://pivotal.io ([https](https://www.gopivotal.com) result 200). * http://maven.apache.org/maven-v4_0_0.xsd with 2 occurrences migrated to: https://maven.apache.org/maven-v4_0_0.xsd ([https](https://maven.apache.org/maven-v4_0_0.xsd) result 301). * http://projects.spring.io/spring-data-mongodb with 1 occurrences migrated to: https://projects.spring.io/spring-data-mongodb ([https](https://projects.spring.io/spring-data-mongodb) result 301). * http://www.pivotal.io with 1 occurrences migrated to: https://www.pivotal.io ([https](https://www.pivotal.io) result 301). # Ignored These URLs were intentionally ignored. * http://maven.apache.org/POM/4.0.0 with 10 occurrences * http://www.w3.org/2001/XMLSchema-instance with 5 occurrences --- pom.xml | 18 +++++++++--------- spring-data-mongodb-benchmarks/pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 2 +- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index f666e1c306..dbe3e93daf 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 @@ -10,7 +10,7 @@ Spring Data MongoDB MongoDB support for Spring Data - http://projects.spring.io/spring-data-mongodb + https://projects.spring.io/spring-data-mongodb org.springframework.data.build @@ -39,7 +39,7 @@ Oliver Gierke ogierke at gopivotal.com Pivotal - http://www.gopivotal.com + https://pivotal.io Project Lead @@ -50,7 +50,7 @@ Thomas Risberg trisberg at vmware.com Pivotal - http://www.gopivotal.com + https://pivotal.io Developer @@ -61,7 +61,7 @@ Mark Pollack mpollack at gopivotal.com Pivotal - http://www.gopivotal.com + https://pivotal.io Developer @@ -72,7 +72,7 @@ Jon Brisbin jbrisbin at gopivotal.com Pivotal - http://www.gopivotal.com + https://pivotal.io Developer @@ -83,7 +83,7 @@ Thomas Darimont tdarimont at gopivotal.com Pivotal - http://www.gopivotal.com + https://pivotal.io Developer @@ -94,7 +94,7 @@ Christoph Strobl cstrobl at gopivotal.com Pivotal - http://www.gopivotal.com + https://pivotal.io Developer @@ -105,7 +105,7 @@ Mark Paluch mpaluch at pivotal.io Pivotal - http://www.pivotal.io + https://www.pivotal.io Developer diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 1d318ea6eb..2dd5b61ffd 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index f38bb28c0f..210e270258 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index a7df819f29..e3354d864a 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index d8a0b253f3..14b8477ee5 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0