From 3fe724bd3214cc572b54390d72474bc800b82843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Beims=20Br=C3=A4scher?= Date: Mon, 17 Aug 2020 07:43:46 -0300 Subject: [PATCH] db: Prevent NullPointerException on GenericDaoBase (#4268) * Prevent NullPointerException on GenericDaoBase * Fix checkstyle: remove unused import --- .../com/cloud/utils/db/GenericDaoBase.java | 26 ++++-- .../cloud/utils/db/GenericDaoBaseTest.java | 88 +++++++++++++------ 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java index f34b8edd64f..1c183ede556 100644 --- a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java +++ b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java @@ -162,6 +162,9 @@ public abstract class GenericDaoBase extends Compone protected static final String SELECT_LAST_INSERT_ID_SQL = "SELECT LAST_INSERT_ID()"; public static final Date DATE_TO_NULL = new Date(Long.MIN_VALUE); + private static final String INTEGRITY_CONSTRAINT_VIOLATION = "23000"; + private static final int DUPLICATE_ENTRY_ERRO_CODE = 1062; + protected static final SequenceFetcher s_seqFetcher = SequenceFetcher.getInstance(); public static GenericDao getDao(Class entityType) { @@ -850,13 +853,23 @@ public abstract class GenericDaoBase extends Compone ub.clear(); return result; } catch (final SQLException e) { - if (e.getSQLState().equals("23000") && e.getErrorCode() == 1062) { - throw new EntityExistsException("Entity already exists ", e); - } + handleEntityExistsException(e); throw new CloudRuntimeException("DB Exception on: " + pstmt, e); } } + /** + * If the SQLException.getSQLState is of 23000 (Integrity Constraint Violation), and the Error Code is 1062 (Duplicate Entry), throws EntityExistsException. + * @throws EntityExistsException + */ + protected static void handleEntityExistsException(SQLException e) throws EntityExistsException { + boolean isIntegrityConstantViolation = INTEGRITY_CONSTRAINT_VIOLATION.equals(e.getSQLState()); + boolean isErrorCodeOfDuplicateEntry = e.getErrorCode() == DUPLICATE_ENTRY_ERRO_CODE; + if (isIntegrityConstantViolation && isErrorCodeOfDuplicateEntry) { + throw new EntityExistsException("Entity already exists ", e); + } + } + @DB() protected Attribute findAttributeByFieldName(String name) { return _allAttributes.get(name); @@ -1450,11 +1463,8 @@ public abstract class GenericDaoBase extends Compone } txn.commit(); } catch (final SQLException e) { - if (e.getSQLState().equals("23000") && e.getErrorCode() == 1062) { - throw new EntityExistsException("Entity already exists: ", e); - } else { - throw new CloudRuntimeException("DB Exception on: " + pstmt, e); - } + handleEntityExistsException(e); + throw new CloudRuntimeException("DB Exception on: " + pstmt, e); } catch (IllegalArgumentException e) { throw new CloudRuntimeException("Problem with getting the ec attribute ", e); } catch (IllegalAccessException e) { diff --git a/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java b/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java index aef0c699032..26530a2f62d 100644 --- a/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java +++ b/framework/db/src/test/java/com/cloud/utils/db/GenericDaoBaseTest.java @@ -20,21 +20,44 @@ import java.sql.ResultSet; import java.sql.SQLException; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.persistence.EntityExistsException; @RunWith(MockitoJUnitRunner.class) public class GenericDaoBaseTest { @Mock ResultSet resultSet; + @Mock + SQLException mockedSQLException; + + private static final String INTEGRITY_CONSTRAINT_VIOLATION = "23000"; + private static final int DUPLICATE_ENTRY_ERRO_CODE = 1062; + + @Before + public void prepareTests() throws SQLException { + Mockito.when(resultSet.getObject(1)).thenReturn(false); + Mockito.when(resultSet.getBoolean(1)).thenReturn(false); + Mockito.when(resultSet.getObject(1)).thenReturn((short) 1); + Mockito.when(resultSet.getShort(1)).thenReturn((short) 1); + Mockito.when(resultSet.getObject(1)).thenReturn(0.1f); + Mockito.when(resultSet.getFloat(1)).thenReturn(0.1f); + Mockito.when(resultSet.getObject(1)).thenReturn(0.1d); + Mockito.when(resultSet.getDouble(1)).thenReturn(0.1d); + Mockito.when(resultSet.getObject(1)).thenReturn(1l); + Mockito.when(resultSet.getLong(1)).thenReturn(1l); + Mockito.when(resultSet.getInt(1)).thenReturn(1); + Mockito.when(resultSet.getObject(1)).thenReturn((byte) 1); + Mockito.when(resultSet.getByte(1)).thenReturn((byte) 1); + } @Test public void getObjectBoolean() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(false); - Mockito.when(resultSet.getBoolean(1)).thenReturn(false); Assert.assertFalse(GenericDaoBase .getObject(Boolean.class, resultSet, 1)); Mockito.verify(resultSet).getBoolean(1); @@ -42,8 +65,6 @@ public class GenericDaoBaseTest { @Test public void getObjectPrimitiveBoolean() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(false); - Mockito.when(resultSet.getBoolean(1)).thenReturn(false); Assert.assertFalse(GenericDaoBase .getObject(boolean.class, resultSet, 1)); Mockito.verify(resultSet).getBoolean(1); @@ -51,8 +72,6 @@ public class GenericDaoBaseTest { @Test public void getObjectPrimitiveShort() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn((short) 1); - Mockito.when(resultSet.getShort(1)).thenReturn((short) 1); Assert.assertEquals(Short.valueOf((short) 1), GenericDaoBase.getObject(short.class, resultSet, 1)); Mockito.verify(resultSet).getShort(1); @@ -60,8 +79,6 @@ public class GenericDaoBaseTest { @Test public void getObjectShort() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn((short) 1); - Mockito.when(resultSet.getShort(1)).thenReturn((short) 1); Assert.assertEquals(Short.valueOf((short) 1), GenericDaoBase.getObject(Short.class, resultSet, 1)); Mockito.verify(resultSet).getShort(1); @@ -69,8 +86,6 @@ public class GenericDaoBaseTest { @Test public void getObjectFloat() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(0.1f); - Mockito.when(resultSet.getFloat(1)).thenReturn(0.1f); Assert.assertEquals(0.1f, GenericDaoBase.getObject(Float.class, resultSet, 1), 0.1); Mockito.verify(resultSet).getFloat(1); @@ -78,8 +93,6 @@ public class GenericDaoBaseTest { @Test public void getObjectPrimitiveFloat() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(0.1f); - Mockito.when(resultSet.getFloat(1)).thenReturn(0.1f); Assert.assertEquals(0.1f, GenericDaoBase.getObject(float.class, resultSet, 1), 0.1); Mockito.verify(resultSet).getFloat(1); @@ -87,8 +100,6 @@ public class GenericDaoBaseTest { @Test public void getObjectPrimitiveDouble() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(0.1d); - Mockito.when(resultSet.getDouble(1)).thenReturn(0.1d); Assert.assertEquals(0.1d, GenericDaoBase.getObject(double.class, resultSet, 1), 0.1); Mockito.verify(resultSet).getDouble(1); @@ -96,8 +107,6 @@ public class GenericDaoBaseTest { @Test public void getObjectDouble() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(0.1d); - Mockito.when(resultSet.getDouble(1)).thenReturn(0.1d); Assert.assertEquals(0.1d, GenericDaoBase.getObject(Double.class, resultSet, 1), 0.1); Mockito.verify(resultSet).getDouble(1); @@ -105,8 +114,6 @@ public class GenericDaoBaseTest { @Test public void getObjectLong() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(1l); - Mockito.when(resultSet.getLong(1)).thenReturn(1l); Assert.assertEquals((Long) 1l, GenericDaoBase.getObject(Long.class, resultSet, 1)); Mockito.verify(resultSet).getLong(1); @@ -114,8 +121,6 @@ public class GenericDaoBaseTest { @Test public void getObjectPrimitiveLong() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(1l); - Mockito.when(resultSet.getLong(1)).thenReturn(1l); Assert.assertEquals((Long) 1l, GenericDaoBase.getObject(long.class, resultSet, 1)); Mockito.verify(resultSet).getLong(1); @@ -123,8 +128,6 @@ public class GenericDaoBaseTest { @Test public void getObjectPrimitiveInt() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(1l); - Mockito.when(resultSet.getInt(1)).thenReturn(1); Assert.assertEquals((Integer) 1, GenericDaoBase.getObject(int.class, resultSet, 1)); Mockito.verify(resultSet).getInt(1); @@ -132,8 +135,6 @@ public class GenericDaoBaseTest { @Test public void getObjectInteger() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn(1l); - Mockito.when(resultSet.getInt(1)).thenReturn(1); Assert.assertEquals((Integer) 1, GenericDaoBase.getObject(Integer.class, resultSet, 1)); Mockito.verify(resultSet).getInt(1); @@ -141,11 +142,44 @@ public class GenericDaoBaseTest { @Test public void getObjectPrimitiveByte() throws SQLException { - Mockito.when(resultSet.getObject(1)).thenReturn((byte) 1); - Mockito.when(resultSet.getByte(1)).thenReturn((byte) 1); Assert.assertTrue((byte) 1 == GenericDaoBase.getObject(byte.class, resultSet, 1)); Mockito.verify(resultSet).getByte(1); } + @Test + public void handleEntityExistsExceptionTestNoMatchForEntityExists() { + Mockito.when(mockedSQLException.getErrorCode()).thenReturn(123); + Mockito.when(mockedSQLException.getSQLState()).thenReturn("123"); + GenericDaoBase.handleEntityExistsException(mockedSQLException); + } + + @Test + public void handleEntityExistsExceptionTestIntegrityConstraint() { + Mockito.when(mockedSQLException.getErrorCode()).thenReturn(123); + Mockito.when(mockedSQLException.getSQLState()).thenReturn(INTEGRITY_CONSTRAINT_VIOLATION); + GenericDaoBase.handleEntityExistsException(mockedSQLException); + } + + @Test + public void handleEntityExistsExceptionTestIntegrityConstraintNull() { + Mockito.when(mockedSQLException.getErrorCode()).thenReturn(123); + Mockito.when(mockedSQLException.getSQLState()).thenReturn(null); + GenericDaoBase.handleEntityExistsException(mockedSQLException); + } + + @Test + public void handleEntityExistsExceptionTestDuplicateEntryErrorCode() { + Mockito.when(mockedSQLException.getErrorCode()).thenReturn(DUPLICATE_ENTRY_ERRO_CODE); + Mockito.when(mockedSQLException.getSQLState()).thenReturn("123"); + GenericDaoBase.handleEntityExistsException(mockedSQLException); + } + + @Test(expected = EntityExistsException.class) + public void handleEntityExistsExceptionTestExpectEntityExistsException() { + Mockito.when(mockedSQLException.getErrorCode()).thenReturn(DUPLICATE_ENTRY_ERRO_CODE); + Mockito.when(mockedSQLException.getSQLState()).thenReturn(INTEGRITY_CONSTRAINT_VIOLATION); + GenericDaoBase.handleEntityExistsException(mockedSQLException); + } + }