From acb6cd56501e19c80e390a15e41cfcf5c143edaa Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 7 Jan 2019 07:46:59 -0800 Subject: [PATCH] Added defensive null checking code to DB Relates to https://github.com/BentoBoxWorld/BentoBox/issues/447 --- .../database/json/JSONDatabaseHandler.java | 26 ++++++++++++++++--- .../mongodb/MongoDBDatabaseHandler.java | 12 ++++++++- .../database/mysql/MySQLDatabaseHandler.java | 10 +++++++ .../database/yaml/YamlDatabaseHandler.java | 21 +++++++++++++++ .../mysql/MySQLDatabaseHandlerTest.java | 16 +++++++----- 5 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java index 62a461264..8f0ce46f2 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/json/JSONDatabaseHandler.java @@ -1,8 +1,5 @@ package world.bentobox.bentobox.database.json; -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.database.DatabaseConnector; - import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.io.File; @@ -19,6 +16,10 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.database.DatabaseConnector; +import world.bentobox.bentobox.database.objects.DataObject; + public class JSONDatabaseHandler extends AbstractJSONDatabaseHandler { private static final String JSON = ".json"; @@ -84,6 +85,15 @@ public class JSONDatabaseHandler extends AbstractJSONDatabaseHandler { @Override public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { + // Null check + if (instance == null) { + plugin.logError("JSON database request to store a null. "); + return; + } + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } String path = DATABASE_FOLDER_NAME + File.separator + dataObject.getSimpleName(); // Obtain the value of uniqueId within the instance (which must be a DataObject) @@ -114,6 +124,16 @@ public class JSONDatabaseHandler extends AbstractJSONDatabaseHandler { @Override public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { + // Null check + if (instance == null) { + plugin.logError("JSON database request to delete a null. "); + return; + } + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } + // Obtain the value of uniqueId within the instance (which must be a DataObject) PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject); Method method = propertyDescriptor.getReadMethod(); diff --git a/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java index e1f8d392a..05e031f61 100644 --- a/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mongodb/MongoDBDatabaseHandler.java @@ -15,8 +15,8 @@ import com.mongodb.client.model.Indexes; import com.mongodb.util.JSON; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler; import world.bentobox.bentobox.database.DatabaseConnector; +import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler; import world.bentobox.bentobox.database.objects.DataObject; /** @@ -78,6 +78,11 @@ public class MongoDBDatabaseHandler extends AbstractJSONDatabaseHandler { @Override public void saveObject(T instance) { + // Null check + if (instance == null) { + plugin.logError("MongoDB database request to store a null. "); + return; + } if (!(instance instanceof DataObject)) { plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); return; @@ -103,6 +108,11 @@ public class MongoDBDatabaseHandler extends AbstractJSONDatabaseHandler { @Override public void deleteObject(T instance) { + // Null check + if (instance == null) { + plugin.logError("MondDB database request to delete a null. "); + return; + } if (!(instance instanceof DataObject)) { plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); return; diff --git a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java index 718f15399..6f1f5bf72 100644 --- a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java @@ -115,6 +115,11 @@ public class MySQLDatabaseHandler extends AbstractJSONDatabaseHandler { @Override public void saveObject(T instance) { + // Null check + if (instance == null) { + plugin.logError("MySQL database request to store a null. "); + return; + } if (!(instance instanceof DataObject)) { plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); return; @@ -138,6 +143,11 @@ public class MySQLDatabaseHandler extends AbstractJSONDatabaseHandler { @Override public void deleteObject(T instance) { + // Null check + if (instance == null) { + plugin.logError("MySQL database request to delete a null. "); + return; + } if (!(instance instanceof DataObject)) { plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); return; diff --git a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java index b209998a0..3a80910e1 100644 --- a/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/yaml/YamlDatabaseHandler.java @@ -34,6 +34,7 @@ import world.bentobox.bentobox.api.configuration.StoreAt; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.database.AbstractDatabaseHandler; import world.bentobox.bentobox.database.DatabaseConnector; +import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.adapters.Adapter; import world.bentobox.bentobox.database.objects.adapters.AdapterInterface; import world.bentobox.bentobox.util.Util; @@ -293,6 +294,16 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { @SuppressWarnings("unchecked") @Override public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { + // Null check + if (instance == null) { + plugin.logError("YAML database request to store a null. "); + return; + } + // DataObject check + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } // This is the Yaml Configuration that will be used and saved at the end YamlConfiguration config = new YamlConfiguration(); @@ -547,6 +558,16 @@ public class YamlDatabaseHandler extends AbstractDatabaseHandler { */ @Override public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException { + // Null check + if (instance == null) { + plugin.logError("YAM database request to delete a null. "); + return; + } + if (!(instance instanceof DataObject)) { + plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); + return; + } + // Obtain the value of uniqueId within the instance (which must be a DataObject) PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject); Method method = propertyDescriptor.getReadMethod(); diff --git a/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java b/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java index 792eb800e..eeab878c1 100644 --- a/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java +++ b/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java @@ -19,7 +19,7 @@ import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.inventory.ItemFactory; import org.bukkit.plugin.PluginManager; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -39,7 +39,7 @@ import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.util.Util; @RunWith(PowerMockRunner.class) -@PrepareForTest( { BentoBox.class, Util.class }) +@PrepareForTest( { Bukkit.class, BentoBox.class, Util.class }) public class MySQLDatabaseHandlerTest { private static MySQLDatabaseHandler handler; @@ -52,8 +52,8 @@ public class MySQLDatabaseHandlerTest { private static IslandWorldManager iwm; - @BeforeClass - public static void setUpBeforeClass() throws Exception { + @Before + public void setUp() throws Exception { Server server = mock(Server.class); world = mock(World.class); when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); @@ -66,8 +66,7 @@ public class MySQLDatabaseHandlerTest { ItemFactory itemFactory = mock(ItemFactory.class); when(server.getItemFactory()).thenReturn(itemFactory); - Bukkit.setServer(server); - + PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); Whitebox.setInternalState(BentoBox.class, "instance", plugin); @@ -100,6 +99,11 @@ public class MySQLDatabaseHandlerTest { } + @Test + public void testSaveNullObject() { + handler.saveObject(null); + } + @Test public void testSaveObject() { handler.saveObject(instance);