diff --git a/src/me/ryanhamshire/GriefPrevention/DatabaseDataStore.java b/src/me/ryanhamshire/GriefPrevention/DatabaseDataStore.java index 78ab34d..064306f 100644 --- a/src/me/ryanhamshire/GriefPrevention/DatabaseDataStore.java +++ b/src/me/ryanhamshire/GriefPrevention/DatabaseDataStore.java @@ -1,32 +1,29 @@ /* - GriefPrevention Server Plugin for Minecraft - Copyright (C) 2012 Ryan Hamshire + GriefPrevention Server Plugin for Minecraft + Copyright (C) 2012 Ryan Hamshire - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ package me.ryanhamshire.GriefPrevention; import java.io.PrintWriter; import java.io.StringWriter; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; +import java.sql.*; import java.text.SimpleDateFormat; import java.util.*; +import java.util.Date; import org.bukkit.*; @@ -34,20 +31,33 @@ import org.bukkit.*; public class DatabaseDataStore extends DataStore { private Connection databaseConnection = null; - + private String databaseUrl; private String userName; private String password; - + + private String updateNameSQL; + private String insertClaimSQL; + private String deleteClaimSQL; + private String getPlayerDataSQL; + private String deletePlayerDataSQL; + private String insertPlayerDataSQL; + private String insertNextClaimIdSQL; + private String deleteGroupBonusSQL; + private String insertSchemaVerSQL; + private String deleteNextClaimIdSQL; + private String deleteSchemaVersionSQL; + private String selectSchemaVersionSQL; + DatabaseDataStore(String url, String userName, String password) throws Exception { this.databaseUrl = url; this.userName = userName; this.password = password; - + this.initialize(); } - + @Override void initialize() throws Exception { @@ -61,7 +71,7 @@ public class DatabaseDataStore extends DataStore GriefPrevention.AddLogEntry("ERROR: Unable to load Java's mySQL database driver. Check to make sure you've installed it properly."); throw e; } - + try { this.refreshDataConnection(); @@ -71,32 +81,32 @@ public class DatabaseDataStore extends DataStore GriefPrevention.AddLogEntry("ERROR: Unable to connect to database. Check your config file settings."); throw e2; } - + try { //ensure the data tables exist Statement statement = databaseConnection.createStatement(); - + statement.execute("CREATE TABLE IF NOT EXISTS griefprevention_nextclaimid (nextid INT(15));"); statement.execute("CREATE TABLE IF NOT EXISTS griefprevention_claimdata (id INT(15), owner VARCHAR(50), lessercorner VARCHAR(100), greatercorner VARCHAR(100), builders TEXT, containers TEXT, accessors TEXT, managers TEXT, inheritnothing BOOLEAN, parentid INT(15));"); - + statement.execute("CREATE TABLE IF NOT EXISTS griefprevention_playerdata (name VARCHAR(50), lastlogin DATETIME, accruedblocks INT(15), bonusblocks INT(15));"); - + statement.execute("CREATE TABLE IF NOT EXISTS griefprevention_schemaversion (version INT(15));"); - + statement.execute("ALTER TABLE griefprevention_claimdata MODIFY builders TEXT;"); statement.execute("ALTER TABLE griefprevention_claimdata MODIFY containers TEXT;"); statement.execute("ALTER TABLE griefprevention_claimdata MODIFY accessors TEXT;"); statement.execute("ALTER TABLE griefprevention_claimdata MODIFY managers TEXT;"); - + //if the next claim id table is empty, this is a brand new database which will write using the latest schema //otherwise, schema version is determined by schemaversion table (or =0 if table is empty, see getSchemaVersion()) ResultSet results = statement.executeQuery("SELECT * FROM griefprevention_nextclaimid;"); - if(!results.next()) + if(!results.next()) { - this.setSchemaVersion(latestSchemaVersion); - } + this.setSchemaVersion(latestSchemaVersion); + } } catch(Exception e3) { @@ -105,29 +115,42 @@ public class DatabaseDataStore extends DataStore e3.printStackTrace(); throw e3; } - + + this.updateNameSQL = "UPDATE griefprevention_playerdata SET name = ? WHERE name = ?;"; + this.insertClaimSQL = "INSERT INTO griefprevention_claimdata (id, owner, lessercorner, greatercorner, builders, containers, accessors, managers, inheritnothing, parentid) VALUES(?,?,?,?,?,?,?,?,?,?);"; + this.deleteClaimSQL = "DELETE FROM griefprevention_claimdata WHERE id=?;"; + this.getPlayerDataSQL = "SELECT * FROM griefprevention_playerdata WHERE name=?;"; + this.deletePlayerDataSQL = "DELETE FROM griefprevention_playerdata WHERE name=?;"; + this.insertPlayerDataSQL = "INSERT INTO griefprevention_playerdata (name, lastlogin, accruedblocks, bonusblocks) VALUES (?,?,?,?);"; + this.insertNextClaimIdSQL = "INSERT INTO griefprevention_nextclaimid VALUES (?);"; + this.deleteGroupBonusSQL = "DELETE FROM griefprevention_playerdata WHERE name=?;"; + this.insertSchemaVerSQL = "INSERT INTO griefprevention_schemaversion VALUES (?)"; + this.deleteNextClaimIdSQL = "DELETE FROM griefprevention_nextclaimid;"; + this.deleteSchemaVersionSQL = "DELETE FROM griefprevention_schemaversion;"; + this.selectSchemaVersionSQL = "SELECT * FROM griefprevention_schemaversion;"; + //load group data into memory Statement statement = databaseConnection.createStatement(); ResultSet results = statement.executeQuery("SELECT * FROM griefprevention_playerdata;"); - + while(results.next()) { String name = results.getString("name"); - + //ignore non-groups. all group names start with a dollar sign. if(!name.startsWith("$")) continue; - + String groupName = name.substring(1); if(groupName == null || groupName.isEmpty()) continue; //defensive coding, avoid unlikely cases - + int groupBonusBlocks = results.getInt("bonusblocks"); - - this.permissionToBonusBlocksMap.put(groupName, groupBonusBlocks); + + this.permissionToBonusBlocksMap.put(groupName, groupBonusBlocks); } - + //load next claim number into memory results = statement.executeQuery("SELECT * FROM griefprevention_nextclaimid;"); - + //if there's nothing yet, add it if(!results.next()) { @@ -140,90 +163,90 @@ public class DatabaseDataStore extends DataStore { this.nextClaimID = results.getLong("nextid"); } - + if(this.getSchemaVersion() == 0) - { - try - { - this.refreshDataConnection(); - - //pull ALL player data from the database - statement = this.databaseConnection.createStatement(); - results = statement.executeQuery("SELECT * FROM griefprevention_playerdata;"); - - //make a list of changes to be made - HashMap changes = new HashMap(); - - ArrayList namesToConvert = new ArrayList(); - while(results.next()) - { - //get the id - String playerName = results.getString("name"); - - //add to list of names to convert to UUID - namesToConvert.add(playerName); - } - - //resolve and cache as many as possible through various means - try - { - UUIDFetcher fetcher = new UUIDFetcher(namesToConvert); - fetcher.call(); - } - catch(Exception e) - { - GriefPrevention.AddLogEntry("Failed to resolve a batch of names to UUIDs. Details:" + e.getMessage()); - e.printStackTrace(); - } - - //reset results cursor - results.beforeFirst(); - - //for each result - while(results.next()) - { - //get the id - String playerName = results.getString("name"); - - //try to convert player name to UUID - try - { - UUID playerID = UUIDFetcher.getUUIDOf(playerName); - - //if successful, update the playerdata row by replacing the player's name with the player's UUID - if(playerID != null) - { - changes.put(playerName, playerID); - } - } - //otherwise leave it as-is. no harm done - it won't be requested by name, and this update only happens once. - catch(Exception ex){ } - } - - //refresh data connection in case data migration took a long time - this.refreshDataConnection(); - - for(String name : changes.keySet()) - { - try - { - statement = this.databaseConnection.createStatement(); - statement.execute("UPDATE griefprevention_playerdata SET name = '" + changes.get(name).toString() + "' WHERE name = '" + name + "';"); - } - catch(SQLException e) - { - GriefPrevention.AddLogEntry("Unable to convert player data for " + name + ". Skipping."); - GriefPrevention.AddLogEntry(e.getMessage()); - } - } - } - catch(SQLException e) - { - GriefPrevention.AddLogEntry("Unable to convert player data. Details:"); - GriefPrevention.AddLogEntry(e.getMessage()); - e.printStackTrace(); - } - } + { + try + { + this.refreshDataConnection(); + + //pull ALL player data from the database + statement = this.databaseConnection.createStatement(); + results = statement.executeQuery("SELECT * FROM griefprevention_playerdata;"); + + //make a list of changes to be made + HashMap changes = new HashMap(); + + ArrayList namesToConvert = new ArrayList(); + while(results.next()) + { + //get the id + String playerName = results.getString("name"); + + //add to list of names to convert to UUID + namesToConvert.add(playerName); + } + + //resolve and cache as many as possible through various means + try + { + UUIDFetcher fetcher = new UUIDFetcher(namesToConvert); + fetcher.call(); + } + catch(Exception e) + { + GriefPrevention.AddLogEntry("Failed to resolve a batch of names to UUIDs. Details:" + e.getMessage()); + e.printStackTrace(); + } + + //reset results cursor + results.beforeFirst(); + + //for each result + while(results.next()) + { + //get the id + String playerName = results.getString("name"); + + //try to convert player name to UUID + try + { + UUID playerID = UUIDFetcher.getUUIDOf(playerName); + + //if successful, update the playerdata row by replacing the player's name with the player's UUID + if(playerID != null) + { + changes.put(playerName, playerID); + } + } + //otherwise leave it as-is. no harm done - it won't be requested by name, and this update only happens once. + catch(Exception ex){ } + } + + //refresh data connection in case data migration took a long time + this.refreshDataConnection(); + + for(String name : changes.keySet()) + { + try (PreparedStatement updateStmnt = this.databaseConnection.prepareStatement(this.getUpdateNameSQL())) { + updateStmnt.setString(1, changes.get(name).toString()); + updateStmnt.setString(2, name); + updateStmnt.executeUpdate(); + } + catch(SQLException e) + { + GriefPrevention.AddLogEntry("Unable to convert player data for " + name + ". Skipping."); + GriefPrevention.AddLogEntry(e.getMessage()); + } + } + } + catch(SQLException e) + { + GriefPrevention.AddLogEntry("Unable to convert player data. Details:"); + GriefPrevention.AddLogEntry(e.getMessage()); + e.printStackTrace(); + } + } if(this.getSchemaVersion() <= 2) { @@ -231,14 +254,14 @@ public class DatabaseDataStore extends DataStore statement.execute("ALTER TABLE griefprevention_claimdata ADD inheritNothing BOOLEAN DEFAULT 0 AFTER managers;"); } - - //load claims data into memory + //load claims data into memory + results = statement.executeQuery("SELECT * FROM griefprevention_claimdata;"); - + ArrayList claimsToRemove = new ArrayList(); ArrayList subdivisionsToLoad = new ArrayList(); List validWorlds = Bukkit.getServer().getWorlds(); - + Long claimID = null; while(results.next()) { @@ -246,11 +269,10 @@ public class DatabaseDataStore extends DataStore { //problematic claims will be removed from secondary storage, and never added to in-memory data store boolean removeClaim = false; - + long parentId = results.getLong("parentid"); claimID = results.getLong("id"); boolean inheritNothing = results.getBoolean("inheritNothing"); - Location lesserBoundaryCorner = null; Location greaterBoundaryCorner = null; String lesserCornerString = "(location not available)"; @@ -258,85 +280,83 @@ public class DatabaseDataStore extends DataStore { lesserCornerString = results.getString("lessercorner"); lesserBoundaryCorner = this.locationFromString(lesserCornerString, validWorlds); - String greaterCornerString = results.getString("greatercorner"); greaterBoundaryCorner = this.locationFromString(greaterCornerString, validWorlds); } catch(Exception e) { - if(e.getMessage() != null && e.getMessage().contains("World not found")) - { - GriefPrevention.AddLogEntry("Failed to load a claim (ID:" + claimID.toString() + ") because its world isn't loaded (yet?). Please delete the claim or contact the GriefPrevention developer with information about which plugin(s) you're using to load or create worlds. " + lesserCornerString); - continue; - } - else - { - throw e; - } + if(e.getMessage() != null && e.getMessage().contains("World not found")) + { + GriefPrevention.AddLogEntry("Failed to load a claim (ID:" + claimID.toString() + ") because its world isn't loaded (yet?). Please delete the claim or contact the GriefPrevention developer with information about which plugin(s) you're using to load or create worlds. " + lesserCornerString); + continue; + } + else + { + throw e; + } } - + String ownerName = results.getString("owner"); UUID ownerID = null; - if(ownerName.isEmpty() || ownerName.startsWith("--")) - { - ownerID = null; //administrative land claim or subdivision - } - else if(this.getSchemaVersion() < 1) - { - try - { - ownerID = UUIDFetcher.getUUIDOf(ownerName); - } - catch(Exception ex) - { - GriefPrevention.AddLogEntry("This owner name did not convert to a UUID: " + ownerName + "."); - GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString()); - } - } - else - { - try - { - ownerID = UUID.fromString(ownerName); - } - catch(Exception ex) - { - GriefPrevention.AddLogEntry("This owner entry is not a UUID: " + ownerName + "."); - GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString()); - } - } - - String buildersString = results.getString("builders"); - List builderNames = Arrays.asList(buildersString.split(";")); - builderNames = this.convertNameListToUUIDList(builderNames); - - String containersString = results.getString("containers"); - List containerNames = Arrays.asList(containersString.split(";")); - containerNames = this.convertNameListToUUIDList(containerNames); - - String accessorsString = results.getString("accessors"); - List accessorNames = Arrays.asList(accessorsString.split(";")); - accessorNames = this.convertNameListToUUIDList(accessorNames); - - String managersString = results.getString("managers"); - List managerNames = Arrays.asList(managersString.split(";")); - managerNames = this.convertNameListToUUIDList(managerNames); - - Claim claim = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, ownerID, builderNames, containerNames, accessorNames, managerNames, inheritNothing, claimID); - - if(removeClaim) + if(ownerName.isEmpty() || ownerName.startsWith("--")) { - claimsToRemove.add(claim); + ownerID = null; //administrative land claim or subdivision } - else if(parentId == -1) + else if(this.getSchemaVersion() < 1) { - //top level claim - this.addClaim(claim, false); + try + { + ownerID = UUIDFetcher.getUUIDOf(ownerName); + } + catch(Exception ex) + { + GriefPrevention.AddLogEntry("This owner name did not convert to a UUID: " + ownerName + "."); + GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString()); + } } else { - //subdivision - subdivisionsToLoad.add(claim); + try + { + ownerID = UUID.fromString(ownerName); + } + catch(Exception ex) + { + GriefPrevention.AddLogEntry("This owner entry is not a UUID: " + ownerName + "."); + GriefPrevention.AddLogEntry(" Converted land claim to administrative @ " + lesserBoundaryCorner.toString()); + } + } + + String buildersString = results.getString("builders"); + List builderNames = Arrays.asList(buildersString.split(";")); + builderNames = this.convertNameListToUUIDList(builderNames); + + String containersString = results.getString("containers"); + List containerNames = Arrays.asList(containersString.split(";")); + containerNames = this.convertNameListToUUIDList(containerNames); + + String accessorsString = results.getString("accessors"); + List accessorNames = Arrays.asList(accessorsString.split(";")); + accessorNames = this.convertNameListToUUIDList(accessorNames); + + String managersString = results.getString("managers"); + List managerNames = Arrays.asList(managersString.split(";")); + managerNames = this.convertNameListToUUIDList(managerNames); + Claim claim = new Claim(lesserBoundaryCorner, greaterBoundaryCorner, ownerID, builderNames, containerNames, accessorNames, managerNames, inheritNothing, claimID); + + if(removeClaim) + { + claimsToRemove.add(claim); + } + else if(parentId == -1) + { + //top level claim + this.addClaim(claim, false); + } + else + { + //subdivision + subdivisionsToLoad.add(claim); } } catch(SQLException e) @@ -345,51 +365,51 @@ public class DatabaseDataStore extends DataStore e.printStackTrace(); } } - + //add subdivisions to their parent claims - for(Claim childClaim : subdivisionsToLoad) - { - //find top level claim parent - Claim topLevelClaim = this.getClaimAt(childClaim.getLesserBoundaryCorner(), true, null); - - if(topLevelClaim == null) - { - claimsToRemove.add(childClaim); - GriefPrevention.AddLogEntry("Removing orphaned claim subdivision: " + childClaim.getLesserBoundaryCorner().toString()); - continue; - } - - //add this claim to the list of children of the current top level claim - childClaim.parent = topLevelClaim; - topLevelClaim.children.add(childClaim); - childClaim.inDataStore = true; - } - - for(int i = 0; i < claimsToRemove.size(); i++) + for(Claim childClaim : subdivisionsToLoad) { - this.deleteClaimFromSecondaryStorage(claimsToRemove.get(i)); + //find top level claim parent + Claim topLevelClaim = this.getClaimAt(childClaim.getLesserBoundaryCorner(), true, null); + + if(topLevelClaim == null) + { + claimsToRemove.add(childClaim); + GriefPrevention.AddLogEntry("Removing orphaned claim subdivision: " + childClaim.getLesserBoundaryCorner().toString()); + continue; + } + + //add this claim to the list of children of the current top level claim + childClaim.parent = topLevelClaim; + topLevelClaim.children.add(childClaim); + childClaim.inDataStore = true; } - + + for(Claim claim : claimsToRemove) + { + this.deleteClaimFromSecondaryStorage(claim); + } + if(this.getSchemaVersion() <= 2) { - this.refreshDataConnection(); - statement = this.databaseConnection.createStatement(); - statement.execute("DELETE FROM griefprevention_claimdata WHERE id='-1';"); + this.refreshDataConnection(); + statement = this.databaseConnection.createStatement(); + statement.execute("DELETE FROM griefprevention_claimdata WHERE id='-1';"); } - + super.initialize(); } - + @Override synchronized void writeClaimToStorage(Claim claim) //see datastore.cs. this will ALWAYS be a top level claim { try { this.refreshDataConnection(); - + //wipe out any existing data about this claim this.deleteClaimFromSecondaryStorage(claim); - + //write claim data to the database this.writeClaimData(claim); } @@ -399,7 +419,7 @@ public class DatabaseDataStore extends DataStore GriefPrevention.AddLogEntry(e.getMessage()); } } - + //actually writes claim data to the database synchronized private void writeClaimData(Claim claim) throws SQLException { @@ -407,67 +427,34 @@ public class DatabaseDataStore extends DataStore String greaterCornerString = this.locationToString(claim.getGreaterBoundaryCorner()); String owner = ""; if(claim.ownerID != null) owner = claim.ownerID.toString(); - + ArrayList builders = new ArrayList(); ArrayList containers = new ArrayList(); ArrayList accessors = new ArrayList(); ArrayList managers = new ArrayList(); - + claim.getPermissions(builders, containers, accessors, managers); - - String buildersString = ""; - for(int i = 0; i < builders.size(); i++) - { - buildersString += builders.get(i) + ";"; - } - - String containersString = ""; - for(int i = 0; i < containers.size(); i++) - { - containersString += containers.get(i) + ";"; - } - - String accessorsString = ""; - for(int i = 0; i < accessors.size(); i++) - { - accessorsString += accessors.get(i) + ";"; - } - String managersString = ""; - for(int i = 0; i < managers.size(); i++) - { - managersString += managers.get(i) + ";"; - } - + String buildersString = this.storageStringBuilder(builders); + String containersString = this.storageStringBuilder(containers); + String accessorsString = this.storageStringBuilder(accessors); + String managersString = this.storageStringBuilder(managers); boolean inheritNothing = claim.getSubclaimRestrictions(); + long parentId = claim.parent == null ? -1 : claim.parent.id; - long parentId; - if(claim.parent == null) - { - parentId = -1; - } - else - { - parentId = claim.parent.id; - } - - try - { - this.refreshDataConnection(); - - Statement statement = databaseConnection.createStatement(); - statement.execute("INSERT INTO griefprevention_claimdata (id, owner, lessercorner, greatercorner, builders, containers, accessors, managers, inheritnothing, parentid) VALUES(" + - claim.id + ", '" + - owner + "', '" + - lesserCornerString + "', '" + - greaterCornerString + "', '" + - buildersString + "', '" + - containersString + "', '" + - accessorsString + "', '" + - managersString + "', " + - inheritNothing + ", "+ - parentId + - ");"); + try (PreparedStatement insertStmt = this.databaseConnection.prepareStatement(this.getInsertClaimSQL())) { + + insertStmt.setLong(1, claim.id); + insertStmt.setString(2, owner); + insertStmt.setString(3, lesserCornerString); + insertStmt.setString(4, greaterCornerString); + insertStmt.setString(5, buildersString); + insertStmt.setString(6, containersString); + insertStmt.setString(7, accessorsString); + insertStmt.setString(8, managersString); + insertStmt.setBoolean(9, inheritNothing); + insertStmt.setLong(10, parentId); + insertStmt.executeUpdate(); } catch(SQLException e) { @@ -475,18 +462,14 @@ public class DatabaseDataStore extends DataStore GriefPrevention.AddLogEntry(e.getMessage()); } } - + //deletes a claim from the database @Override synchronized void deleteClaimFromSecondaryStorage(Claim claim) { - try - { - this.refreshDataConnection(); - - - Statement statement = this.databaseConnection.createStatement(); - statement.execute("DELETE FROM griefprevention_claimdata WHERE id='" + claim.id + "';"); + try(PreparedStatement deleteStmnt = this.databaseConnection.prepareStatement(this.getDeleteClaimSQL())) { + deleteStmnt.setLong(1, claim.id); + deleteStmnt.executeUpdate(); } catch(SQLException e) { @@ -495,88 +478,86 @@ public class DatabaseDataStore extends DataStore e.printStackTrace(); } } - + @Override synchronized PlayerData getPlayerDataFromStorage(UUID playerID) { PlayerData playerData = new PlayerData(); playerData.playerID = playerID; - - try + + try (PreparedStatement selectStmnt = this.databaseConnection.prepareStatement( this.getGetPlayerDataSQL())) { - this.refreshDataConnection(); - - Statement statement = this.databaseConnection.createStatement(); - ResultSet results = statement.executeQuery("SELECT * FROM griefprevention_playerdata WHERE name='" + playerID.toString() + "';"); - + selectStmnt.setString(1, playerID.toString()); + ResultSet results = selectStmnt.executeQuery(); + //if data for this player exists, use it if(results.next()) { playerData.setAccruedClaimBlocks(results.getInt("accruedblocks")); - playerData.setBonusClaimBlocks(results.getInt("bonusblocks")); + playerData.setBonusClaimBlocks(results.getInt("bonusblocks")); } } catch(SQLException e) { - StringWriter errors = new StringWriter(); - e.printStackTrace(new PrintWriter(errors)); - GriefPrevention.AddLogEntry(playerID + " " + errors.toString(), CustomLogEntryTypes.Exception); + StringWriter errors = new StringWriter(); + e.printStackTrace(new PrintWriter(errors)); + GriefPrevention.AddLogEntry(playerID + " " + errors.toString(), CustomLogEntryTypes.Exception); } - + return playerData; } - + //saves changes to player data. MUST be called after you're done making changes, otherwise a reload will lose them @Override public void overrideSavePlayerData(UUID playerID, PlayerData playerData) { //never save data for the "administrative" account. an empty string for player name indicates administrative account if(playerID == null) return; - + this.savePlayerData(playerID.toString(), playerData); } - + private void savePlayerData(String playerID, PlayerData playerData) { - try - { - this.refreshDataConnection(); + try (PreparedStatement deleteStmnt = this.databaseConnection.prepareStatement(this.getDeletePlayerDataSQL()); + PreparedStatement insertStmnt = this.databaseConnection.prepareStatement(this.getInsertPlayerDataSQL())) { OfflinePlayer player = Bukkit.getOfflinePlayer(UUID.fromString(playerID)); - + SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = sqlFormat.format(new Date(player.getLastPlayed())); - - Statement statement = databaseConnection.createStatement(); - statement.execute("DELETE FROM griefprevention_playerdata WHERE name='" + playerID.toString() + "';"); - statement = databaseConnection.createStatement(); - statement.execute("INSERT INTO griefprevention_playerdata (name, lastlogin, accruedblocks, bonusblocks) VALUES ('" + playerID.toString() + "', '" + dateString + "', " + playerData.getAccruedClaimBlocks() + ", " + playerData.getBonusClaimBlocks() + ");"); + deleteStmnt.setString(1, playerID); + deleteStmnt.executeUpdate(); + + insertStmnt.setString(1, playerID); + insertStmnt.setString(2, dateString); + insertStmnt.setInt(3, playerData.getAccruedClaimBlocks()); + insertStmnt.setInt(4, playerData.getBonusClaimBlocks()); + insertStmnt.executeUpdate(); } catch(SQLException e) { - StringWriter errors = new StringWriter(); - e.printStackTrace(new PrintWriter(errors)); - GriefPrevention.AddLogEntry(playerID + " " + errors.toString(), CustomLogEntryTypes.Exception); + StringWriter errors = new StringWriter(); + e.printStackTrace(new PrintWriter(errors)); + GriefPrevention.AddLogEntry(playerID + " " + errors.toString(), CustomLogEntryTypes.Exception); } } - + @Override synchronized void incrementNextClaimID() { this.setNextClaimID(this.nextClaimID + 1); } - + //sets the next claim ID. used by incrementNextClaimID() above, and also while migrating data from a flat file data store synchronized void setNextClaimID(long nextID) { this.nextClaimID = nextID; - - try - { - this.refreshDataConnection(); - - Statement statement = databaseConnection.createStatement(); - statement.execute("DELETE FROM griefprevention_nextclaimid;"); - statement.execute("INSERT INTO griefprevention_nextclaimid VALUES (" + nextID + ");"); + + try (PreparedStatement deleteStmnt = this.databaseConnection.prepareStatement(this.getDeleteNextClaimIdSQL()); + PreparedStatement insertStmnt = this.databaseConnection.prepareStatement(this.getInsertNextClaimIdSQL())) { + deleteStmnt.execute(); + insertStmnt.setLong(1, nextID); + insertStmnt.executeUpdate(); } catch(SQLException e) { @@ -584,31 +565,32 @@ public class DatabaseDataStore extends DataStore GriefPrevention.AddLogEntry(e.getMessage()); } } - + //updates the database with a group's bonus blocks @Override synchronized void saveGroupBonusBlocks(String groupName, int currentValue) { //group bonus blocks are stored in the player data table, with player name = $groupName - try - { - this.refreshDataConnection(); - - SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - String dateString = sqlFormat.format(new Date()); - - Statement statement = databaseConnection.createStatement(); - statement.execute("DELETE FROM griefprevention_playerdata WHERE name='$" + groupName + "';"); - statement = databaseConnection.createStatement(); - statement.execute("INSERT INTO griefprevention_playerdata (name, lastlogin, accruedblocks, bonusblocks) VALUES ('$" + groupName + "', '" + dateString + "', " + "0" + ", " + String.valueOf(currentValue) + ");"); - } - catch(SQLException e) - { - GriefPrevention.AddLogEntry("Unable to save data for group " + groupName + ". Details:"); - GriefPrevention.AddLogEntry(e.getMessage()); - } + try (PreparedStatement deleteStmnt = this.databaseConnection.prepareStatement(this.getDeleteGroupBonusSQL()); + PreparedStatement insertStmnt = this.databaseConnection.prepareStatement(this.getInsertPlayerDataSQL())) { + SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String dateString = sqlFormat.format(new Date()); + deleteStmnt.setString(1, '$' + groupName); + deleteStmnt.executeUpdate(); + + insertStmnt.setString(1, '$' + groupName); + insertStmnt.setString(2, dateString); + insertStmnt.setInt(3, 0); + insertStmnt.setInt(4, currentValue); + insertStmnt.executeUpdate(); + } + catch(SQLException e) + { + GriefPrevention.AddLogEntry("Unable to save data for group " + groupName + ". Details:"); + GriefPrevention.AddLogEntry(e.getMessage()); + } } - + @Override synchronized void close() { @@ -623,79 +605,133 @@ public class DatabaseDataStore extends DataStore } catch(SQLException e){}; } - + this.databaseConnection = null; } - + private synchronized void refreshDataConnection() throws SQLException { if(this.databaseConnection == null || !this.databaseConnection.isValid(3)) { if(this.databaseConnection != null && !this.databaseConnection.isClosed()) - { - this.databaseConnection.close(); - } - - //set username/pass properties + { + this.databaseConnection.close(); + } + + //set username/pass properties Properties connectionProps = new Properties(); connectionProps.put("user", this.userName); connectionProps.put("password", this.password); connectionProps.put("autoReconnect", "true"); connectionProps.put("maxReconnects", String.valueOf(Integer.MAX_VALUE)); - + //establish connection - this.databaseConnection = DriverManager.getConnection(this.databaseUrl, connectionProps); + this.databaseConnection = DriverManager.getConnection(this.databaseUrl, connectionProps); } } - @Override - protected int getSchemaVersionFromStorage() - { - try - { - this.refreshDataConnection(); - - Statement statement = this.databaseConnection.createStatement(); - ResultSet results = statement.executeQuery("SELECT * FROM griefprevention_schemaversion;"); - - //if there's nothing yet, assume 0 and add it - if(!results.next()) - { - this.setSchemaVersion(0); - return 0; - } - - //otherwise return the value that's in the table - else - { - return results.getInt("version"); - } - - } - catch(SQLException e) - { - GriefPrevention.AddLogEntry("Unable to retrieve schema version from database. Details:"); - GriefPrevention.AddLogEntry(e.getMessage()); - e.printStackTrace(); - return 0; - } - } + @Override + protected int getSchemaVersionFromStorage() + { + try (PreparedStatement selectStmnt = this.databaseConnection.prepareStatement(this.getSelectSchemaVersionSQL())) { + ResultSet results = selectStmnt.executeQuery(); - @Override - protected void updateSchemaVersionInStorage(int versionToSet) - { - try - { - this.refreshDataConnection(); - - Statement statement = databaseConnection.createStatement(); - statement.execute("DELETE FROM griefprevention_schemaversion;"); - statement.execute("INSERT INTO griefprevention_schemaversion VALUES (" + versionToSet + ");"); - } - catch(SQLException e) - { - GriefPrevention.AddLogEntry("Unable to set next schema version to " + versionToSet + ". Details:"); - GriefPrevention.AddLogEntry(e.getMessage()); - } - } + //if there's nothing yet, assume 0 and add it + if(!results.next()) + { + this.setSchemaVersion(0); + return 0; + } + //otherwise return the value that's in the table + else + { + return results.getInt("version"); + } + } + catch(SQLException e) + { + GriefPrevention.AddLogEntry("Unable to retrieve schema version from database. Details:"); + GriefPrevention.AddLogEntry(e.getMessage()); + e.printStackTrace(); + return 0; + } + } + + @Override + protected void updateSchemaVersionInStorage(int versionToSet) + { + try (PreparedStatement deleteStmnt = this.databaseConnection.prepareStatement(this.getDeleteSchemaVersionSQL()); + PreparedStatement insertStmnt = this.databaseConnection.prepareStatement(this.getInsertSchemaVerSQL())) { + deleteStmnt.execute(); + + insertStmnt.setInt(1, versionToSet); + insertStmnt.executeUpdate(); + } + catch(SQLException e) + { + GriefPrevention.AddLogEntry("Unable to set next schema version to " + versionToSet + ". Details:"); + GriefPrevention.AddLogEntry(e.getMessage()); + } + } + + /** + * Concats an array to a string divided with the ; sign + * @param input Arraylist with strings to concat + * @return String with all values from input array + */ + private String storageStringBuilder(ArrayList input) { + String output = ""; + for(String string : input) { + output += string + ";"; + } + return output; + } + + public String getUpdateNameSQL() { + return updateNameSQL; + } + + public String getInsertClaimSQL() { + return insertClaimSQL; + } + + public String getDeleteClaimSQL() { + return deleteClaimSQL; + } + + public String getGetPlayerDataSQL() { + return getPlayerDataSQL; + } + + public String getDeletePlayerDataSQL() { + return deletePlayerDataSQL; + } + + public String getInsertPlayerDataSQL() { + return insertPlayerDataSQL; + } + + public String getInsertNextClaimIdSQL() { + return insertNextClaimIdSQL; + } + + public String getDeleteGroupBonusSQL() { + return deleteGroupBonusSQL; + } + + public String getInsertSchemaVerSQL() { + return insertSchemaVerSQL; + } + + public String getDeleteNextClaimIdSQL() { + return deleteNextClaimIdSQL; + } + + public String getDeleteSchemaVersionSQL() { + return deleteSchemaVersionSQL; + } + + public String getSelectSchemaVersionSQL() { + return selectSchemaVersionSQL; + } }