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 0bff447c4..674a2e1a9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 the original author or authors. + * Copyright 2010-2013 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. @@ -15,12 +15,6 @@ */ package org.springframework.data.mongodb.core; -import com.mongodb.MongoException; -import com.mongodb.MongoException.CursorNotFound; -import com.mongodb.MongoException.DuplicateKey; -import com.mongodb.MongoException.Network; -import com.mongodb.MongoInternalException; - import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DuplicateKeyException; @@ -29,21 +23,26 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.UncategorizedMongoDbException; +import com.mongodb.MongoException; +import com.mongodb.MongoException.CursorNotFound; +import com.mongodb.MongoException.DuplicateKey; +import com.mongodb.MongoException.Network; +import com.mongodb.MongoInternalException; + /** * Simple {@link PersistenceExceptionTranslator} for Mongo. Convert the given runtime exception to an appropriate * exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is * appropriate: any other exception may have resulted from user code, and should not be translated. * * @author Oliver Gierke + * @author Michal Vich */ public class MongoExceptionTranslator implements PersistenceExceptionTranslator { /* - * (non-Javadoc) - * - * @see org.springframework.dao.support.PersistenceExceptionTranslator# - * translateExceptionIfPossible(java.lang.RuntimeException) - */ + * (non-Javadoc) + * @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException) + */ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { // Check for well-known MongoException subclasses. @@ -52,14 +51,23 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator if (ex instanceof DuplicateKey) { return new DuplicateKeyException(ex.getMessage(), ex); } + if (ex instanceof Network) { return new DataAccessResourceFailureException(ex.getMessage(), ex); } + if (ex instanceof CursorNotFound) { return new DataAccessResourceFailureException(ex.getMessage(), ex); } + + if (ex instanceof MongoInternalException) { + return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex); + } + if (ex instanceof MongoException) { + int code = ((MongoException) ex).getCode(); + if (code == 11000 || code == 11001) { throw new DuplicateKeyException(ex.getMessage(), ex); } else if (code == 12000 || code == 13440) { @@ -69,9 +77,6 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator } return new UncategorizedMongoDbException(ex.getMessage(), ex); } - if (ex instanceof MongoInternalException) { - return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex); - } // If we get here, we have an exception that resulted from user code, // rather than the persistence provider, so we return null to indicate 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 new file mode 100644 index 000000000..5d155d9ef --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java @@ -0,0 +1,159 @@ +/* + * Copyright 2013 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.core; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.net.UnknownHostException; + +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.DuplicateKeyException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.dao.InvalidDataAccessResourceUsageException; +import org.springframework.data.mongodb.UncategorizedMongoDbException; + +import com.mongodb.MongoException; +import com.mongodb.MongoException.DuplicateKey; +import com.mongodb.MongoException.Network; +import com.mongodb.MongoInternalException; +import com.mongodb.ServerAddress; + +/** + * Unit tests for {@link MongoExceptionTranslator}. + * + * @author Michal Vich + * @author Oliver Gierke + */ +public class MongoExceptionTranslatorUnitTests { + + MongoExceptionTranslator translator; + + @Before + public void setUp() { + translator = new MongoExceptionTranslator(); + } + + @Test + public void translateDuplicateKey() { + + DuplicateKey exception = new DuplicateKey(1, "Duplicated key"); + DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + + expectExceptionWithCauseMessage(translatedException, DuplicateKeyException.class, "Duplicated key"); + } + + @Test + public void translateNetwork() { + + Network exception = new Network("IOException", new IOException("IOException")); + DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + + expectExceptionWithCauseMessage(translatedException, DataAccessResourceFailureException.class, "IOException"); + + } + + @Test + public void translateCursorNotFound() throws UnknownHostException { + + MongoException.CursorNotFound exception = new MongoException.CursorNotFound(1, new ServerAddress()); + DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + + expectExceptionWithCauseMessage(translatedException, DataAccessResourceFailureException.class); + } + + @Test + public void translateToDuplicateKeyException() { + + checkTranslatedMongoException(DuplicateKeyException.class, 11000); + checkTranslatedMongoException(DuplicateKeyException.class, 11001); + } + + @Test + public void translateToDataAccessResourceFailureException() { + + checkTranslatedMongoException(DataAccessResourceFailureException.class, 12000); + checkTranslatedMongoException(DataAccessResourceFailureException.class, 13440); + } + + @Test + public void translateToInvalidDataAccessApiUsageException() { + + checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 10003); + checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12001); + checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12010); + checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12011); + checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12012); + } + + @Test + public void translateToUncategorizedMongoDbException() { + + MongoException exception = new MongoException(0, ""); + DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + + expectExceptionWithCauseMessage(translatedException, UncategorizedMongoDbException.class); + } + + @Test + public void translateMongoInternalException() { + + MongoInternalException exception = new MongoInternalException("Internal exception"); + DataAccessException translatedException = translator.translateExceptionIfPossible(exception); + + expectExceptionWithCauseMessage(translatedException, InvalidDataAccessResourceUsageException.class); + } + + @Test + public void translateUnsupportedException() { + + RuntimeException exception = new RuntimeException(); + assertThat(translator.translateExceptionIfPossible(exception), is(nullValue())); + } + + private void checkTranslatedMongoException(Class clazz, int code) { + + try { + translator.translateExceptionIfPossible(new MongoException(code, "")); + fail("Expected exception of type " + clazz.getName() + "!"); + } catch (NestedRuntimeException e) { + Throwable cause = e.getRootCause(); + assertThat(cause, is(instanceOf(MongoException.class))); + assertThat(((MongoException) cause).getCode(), is(code)); + } + } + + private static void expectExceptionWithCauseMessage(NestedRuntimeException e, + Class type) { + expectExceptionWithCauseMessage(e, type, null); + } + + private static void expectExceptionWithCauseMessage(NestedRuntimeException e, + Class type, String message) { + + assertThat(e, is(instanceOf(type))); + + if (message != null) { + assertThat(e.getRootCause(), is(notNullValue())); + assertThat(e.getRootCause().getMessage(), containsString(message)); + } + } +}