From a3066803d043c59fc3250e35ce8e00f1fa2b7441 Mon Sep 17 00:00:00 2001 From: t00thpick1 Date: Thu, 24 Jul 2014 23:27:56 -0400 Subject: [PATCH] Completely untested, needs some more work as follows: A. Handle reconnecting on full disconnects. B. refactor code to use same connection object in same task(Some tasks go through many methods that each get their own connection object) C. Test that it actually works :P --- pom.xml | 10 + .../nossr50/database/SQLDatabaseManager.java | 946 ++++++++---------- 2 files changed, 452 insertions(+), 504 deletions(-) diff --git a/pom.xml b/pom.xml index 13b794059..6a1de4990 100755 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ com.turt2live.metrics:MetricsExtension + net.snaq:dbpool @@ -83,6 +84,10 @@ com.turt2live.metrics com.gmail.nossr50.metrics.mcstats + + net.snaq + com.gmail.nossr50.dbpool + @@ -136,6 +141,11 @@ MetricsExtension 0.0.5-SNAPSHOT + + net.snaq + dbpool + 5.1 + diff --git a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java index 0ee9e1b90..09d256167 100644 --- a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java @@ -1,6 +1,7 @@ package com.gmail.nossr50.database; import java.sql.Connection; +import java.sql.Driver; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -15,6 +16,8 @@ import java.util.Map; import java.util.Properties; import java.util.UUID; +import snaq.db.ConnectionPool; + import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; import com.gmail.nossr50.datatypes.MobHealthbarType; @@ -25,44 +28,53 @@ import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.skills.AbilityType; import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.runnables.database.SQLDatabaseKeepaliveTask; -import com.gmail.nossr50.runnables.database.SQLReconnectTask; import com.gmail.nossr50.runnables.database.UUIDUpdateAsyncTask; import com.gmail.nossr50.util.Misc; public final class SQLDatabaseManager implements DatabaseManager { private String connectionString; private String tablePrefix = Config.getInstance().getMySQLTablePrefix(); - private Connection connection = null; - - // Scale waiting time by this much per failed attempt - private final double SCALING_FACTOR = 40.0; - - // Minimum wait in nanoseconds (default 500ms) - private final long MIN_WAIT = 500L * 1000000L; - - // Maximum time to wait between reconnects (default 5 minutes) - private final long MAX_WAIT = 5L * 60L * 1000L * 1000000L; // How long to wait when checking if connection is valid (default 3 seconds) private final int VALID_TIMEOUT = 3; - // When next to try connecting to Database in nanoseconds - private long nextReconnectTimestamp = 0L; - - // How many connection attempts have failed - private int reconnectAttempt = 0; + private ConnectionPool connectionPool; protected SQLDatabaseManager() { + connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName(); + + try { + // Force driver to load if not yet loaded + Class c = Class.forName("com.mysql.jdbc.Driver"); + Driver driver = (Driver) c.newInstance(); + DriverManager.registerDriver(driver); + Properties connectionProperties = new Properties(); + connectionProperties.put("user", Config.getInstance().getMySQLUserName()); + connectionProperties.put("password", Config.getInstance().getMySQLUserPassword()); + connectionProperties.put("autoReconnect", "false"); + connectionPool = new ConnectionPool("mcMMO-Pool", 1 /*Minimum of one*/, 10 /*max pool size Configurable?*/, 10/*max num connections Configurable?*/, 0 /* idle timeout of connections */, connectionString, connectionProperties); + connectionPool.init(); // Init first connection + connectionPool.registerShutdownHook(); // Auto release when done + } catch (ClassNotFoundException e) { + // TODO tft do something here + e.printStackTrace(); + } catch (SQLException e) { + // TODO tft do something here + e.printStackTrace(); + } catch (InstantiationException e) { + // TODO tft do something here + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO tft do something here + e.printStackTrace(); + } + checkStructure(); new SQLDatabaseKeepaliveTask(this).runTaskTimerAsynchronously(mcMMO.p, 10, 60L * 60 * Misc.TICK_CONVERSION_FACTOR); } public void purgePowerlessUsers() { - if (!checkConnected()) { - return; - } - mcMMO.p.getLogger().info("Purging powerless users..."); Collection> usernames = read("SELECT u.user FROM " + tablePrefix + "skills AS s, " + tablePrefix + "users AS u WHERE s.user_id = u.id AND (s.taming+s.mining+s.woodcutting+s.repair+s.unarmed+s.herbalism+s.excavation+s.archery+s.swords+s.axes+s.acrobatics+s.fishing) = 0").values(); @@ -79,10 +91,6 @@ public final class SQLDatabaseManager implements DatabaseManager { } public void purgeOldUsers() { - if (!checkConnected()) { - return; - } - long currentTime = System.currentTimeMillis(); mcMMO.p.getLogger().info("Purging old users..."); @@ -101,10 +109,6 @@ public final class SQLDatabaseManager implements DatabaseManager { } public boolean removeUser(String playerName) { - if (!checkConnected()) { - return false; - } - boolean success = update("DELETE FROM u, e, h, s, c " + "USING " + tablePrefix + "users u " + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " + @@ -119,10 +123,6 @@ public final class SQLDatabaseManager implements DatabaseManager { } public boolean saveUser(PlayerProfile profile) { - if (!checkConnected()) { - return false; - } - int userId = readId(profile.getPlayerName()); if (userId == -1) { newUser(profile.getPlayerName(), profile.getUniqueId().toString()); @@ -197,38 +197,42 @@ public final class SQLDatabaseManager implements DatabaseManager { public List readLeaderboard(SkillType skill, int pageNumber, int statsPerPage) { List stats = new ArrayList(); - if (checkConnected()) { - String query = skill == null ? "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy" : skill.name().toLowerCase(); - ResultSet resultSet; - PreparedStatement statement = null; + String query = skill == null ? "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy" : skill.name().toLowerCase(); + ResultSet resultSet; + PreparedStatement statement = null; + Connection connection = null; - try { - statement = connection.prepareStatement("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT ?, ?"); - statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage); - statement.setInt(2, statsPerPage); - resultSet = statement.executeQuery(); + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); + statement = connection.prepareStatement("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT ?, ?"); + statement.setInt(1, (pageNumber * statsPerPage) - statsPerPage); + statement.setInt(2, statsPerPage); + resultSet = statement.executeQuery(); - while (resultSet.next()) { - ArrayList column = new ArrayList(); + while (resultSet.next()) { + ArrayList column = new ArrayList(); - for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { - column.add(resultSet.getString(i)); - } + for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { + column.add(resultSet.getString(i)); + } - stats.add(new PlayerStat(column.get(1), Integer.valueOf(column.get(0)))); + stats.add(new PlayerStat(column.get(1), Integer.valueOf(column.get(0)))); + } + } catch (SQLException ex) { + printErrors(ex); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + // Ignore } } - catch (SQLException ex) { - printErrors(ex); - } - finally { - if (statement != null) { - try { - statement.close(); - } - catch (SQLException e) { - // Ignore - } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + // Ignore } } } @@ -239,48 +243,16 @@ public final class SQLDatabaseManager implements DatabaseManager { public Map readRank(String playerName) { Map skills = new HashMap(); - if (checkConnected()) { - ResultSet resultSet; + ResultSet resultSet; + Connection connection = null; - try { - for (SkillType skillType : SkillType.NON_CHILD_SKILLS) { - String skillName = skillType.name().toLowerCase(); - String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " + - "AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE user = ?)"; - - PreparedStatement statement = connection.prepareStatement(sql); - statement.setString(1, playerName); - resultSet = statement.executeQuery(); - - resultSet.next(); - - int rank = resultSet.getInt("rank"); - - sql = "SELECT user, " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " + - "AND " + skillName + " = (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE user = '" + playerName + "') ORDER BY user"; - - statement.close(); - - statement = connection.prepareStatement(sql); - resultSet = statement.executeQuery(); - - while (resultSet.next()) { - if (resultSet.getString("user").equalsIgnoreCase(playerName)) { - skills.put(skillType, rank + resultSet.getRow()); - break; - } - } - - statement.close(); - } - - String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " + - "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > " + - "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + - "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)"; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); + for (SkillType skillType : SkillType.NON_CHILD_SKILLS) { + String skillName = skillType.name().toLowerCase(); + String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " + + "AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + + "WHERE user = ?)"; PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, playerName); @@ -290,30 +262,69 @@ public final class SQLDatabaseManager implements DatabaseManager { int rank = resultSet.getInt("rank"); + sql = "SELECT user, " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " + + "AND " + skillName + " = (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + + "WHERE user = '" + playerName + "') ORDER BY user"; + statement.close(); - sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + - "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " + - "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy = " + - "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + - "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user"; - statement = connection.prepareStatement(sql); - statement.setString(1, playerName); resultSet = statement.executeQuery(); while (resultSet.next()) { if (resultSet.getString("user").equalsIgnoreCase(playerName)) { - skills.put(null, rank + resultSet.getRow()); + skills.put(skillType, rank + resultSet.getRow()); break; } } statement.close(); } - catch (SQLException ex) { - printErrors(ex); + + String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + + "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " + + "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > " + + "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)"; + + PreparedStatement statement = connection.prepareStatement(sql); + statement.setString(1, playerName); + resultSet = statement.executeQuery(); + + resultSet.next(); + + int rank = resultSet.getInt("rank"); + + statement.close(); + + sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + + "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " + + "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy = " + + "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user"; + + statement = connection.prepareStatement(sql); + statement.setString(1, playerName); + resultSet = statement.executeQuery(); + + while (resultSet.next()) { + if (resultSet.getString("user").equalsIgnoreCase(playerName)) { + skills.put(null, rank + resultSet.getRow()); + break; + } + } + + statement.close(); + } catch (SQLException ex) { + printErrors(ex); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + // Ignore + } } } @@ -321,13 +332,11 @@ public final class SQLDatabaseManager implements DatabaseManager { } public void newUser(String playerName, String uuid) { - if (!checkConnected()) { - return; - } - PreparedStatement statement = null; + Connection connection = null; try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement("INSERT INTO " + tablePrefix + "users (user, uuid, lastlogin) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS); statement.setString(1, playerName); statement.setString(2, uuid); @@ -336,16 +345,20 @@ public final class SQLDatabaseManager implements DatabaseManager { int id = readId(playerName); writeMissingRows(id); - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -353,22 +366,15 @@ public final class SQLDatabaseManager implements DatabaseManager { } /** - * This is a fallback method to provide the old way of getting a PlayerProfile - * in case there is no UUID match found + * This is a fallback method to provide the old way of getting a + * PlayerProfile in case there is no UUID match found */ private PlayerProfile loadPlayerNameProfile(String playerName, String uuid, boolean create, boolean retry) { - if (!checkConnected()) { - // return fake profile if not connected - if (uuid.isEmpty()) { - return new PlayerProfile(playerName, false); - } - - return new PlayerProfile(playerName, UUID.fromString(uuid), false); - } - PreparedStatement statement = null; + Connection connection = null; try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement( "SELECT " + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, " @@ -390,21 +396,24 @@ public final class SQLDatabaseManager implements DatabaseManager { PlayerProfile ret = loadFromResult(playerName, result); result.close(); return ret; - } - catch (SQLException e) { + } catch (SQLException e) { } } result.close(); - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -451,18 +460,11 @@ public final class SQLDatabaseManager implements DatabaseManager { } private PlayerProfile loadPlayerProfile(String playerName, String uuid, boolean create, boolean retry) { - if (!checkConnected()) { - // return fake profile if not connected - if (uuid.isEmpty()) { - return new PlayerProfile(playerName, false); - } - - return new PlayerProfile(playerName, UUID.fromString(uuid), false); - } - PreparedStatement statement = null; + Connection connection = null; try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement( "SELECT " + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, " @@ -483,6 +485,7 @@ public final class SQLDatabaseManager implements DatabaseManager { try { PlayerProfile profile = loadFromResult(playerName, result); result.close(); + statement.close(); if (!playerName.isEmpty() && !profile.getPlayerName().isEmpty()) { statement = connection.prepareStatement( @@ -496,21 +499,24 @@ public final class SQLDatabaseManager implements DatabaseManager { } return profile; - } - catch (SQLException e) { + } catch (SQLException e) { } } result.close(); - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -544,13 +550,11 @@ public final class SQLDatabaseManager implements DatabaseManager { } public void convertUsers(DatabaseManager destination) { - if (!checkConnected()) { - return; - } - PreparedStatement statement = null; + Connection connection = null; try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement( "SELECT " + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, " @@ -574,23 +578,26 @@ public final class SQLDatabaseManager implements DatabaseManager { resultSet.next(); destination.saveUser(loadFromResult(playerName, resultSet)); resultSet.close(); - } - catch (SQLException e) { + } catch (SQLException e) { // Ignore } convertedUsers++; Misc.printProgress(convertedUsers, progressInterval, startMillis); } - } - catch (SQLException e) { + } catch (SQLException e) { printErrors(e); - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -599,14 +606,11 @@ public final class SQLDatabaseManager implements DatabaseManager { } public boolean saveUserUUID(String userName, UUID uuid) { - if (!checkConnected()) { - // return false - return false; - } - PreparedStatement statement = null; + Connection connection = null; try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement( "UPDATE `" + tablePrefix + "users` SET " + " uuid = ? WHERE user = ?"); @@ -614,17 +618,21 @@ public final class SQLDatabaseManager implements DatabaseManager { statement.setString(2, userName); statement.execute(); return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -634,14 +642,13 @@ public final class SQLDatabaseManager implements DatabaseManager { } public boolean saveUserUUIDs(Map fetchedUUIDs) { - if (!checkConnected()) { - return false; - } - PreparedStatement statement = null; int count = 0; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE user = ?"); for (Map.Entry entry : fetchedUUIDs.entrySet()) { @@ -663,137 +670,56 @@ public final class SQLDatabaseManager implements DatabaseManager { } return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } } } - /** - * Check connection status and re-establish if dead or stale. - *

- * If the very first immediate attempt fails, further attempts - * will be made in progressively larger intervals up to MAX_WAIT - * intervals. - *

- * This allows for MySQL to time out idle connections as needed by - * server operator, without affecting McMMO, while still providing - * protection against a database outage taking down Bukkit's tick - * processing loop due to attempting a database connection each - * time McMMO needs the database. - * - * @return the boolean value for whether or not we are connected - */ - public boolean checkConnected() { - boolean isClosed = true; - boolean isValid = false; - boolean exists = (connection != null); - - // If we're waiting for server to recover then leave early - if (nextReconnectTimestamp > 0 && nextReconnectTimestamp > System.nanoTime()) { - return false; - } - - if (exists) { - try { - isClosed = connection.isClosed(); - } - catch (SQLException e) { - isClosed = true; - e.printStackTrace(); - printErrors(e); - } - - if (!isClosed) { - try { - isValid = connection.isValid(VALID_TIMEOUT); - } - catch (SQLException e) { - // Don't print stack trace because it's valid to lose idle connections to the server and have to restart them. - isValid = false; - } - } - } - - // Leave if all ok - if (exists && !isClosed && isValid) { - // Housekeeping - nextReconnectTimestamp = 0; - reconnectAttempt = 0; - return true; - } - - // Cleanup after ourselves for GC and MySQL's sake - if (exists && !isClosed) { - try { - connection.close(); - } - catch (SQLException ex) { - // This is a housekeeping exercise, ignore errors - } - } - - // Try to connect again - connect(); - - // Leave if connection is good - try { - if (connection != null && !connection.isClosed()) { - // Schedule a database save if we really had an outage - if (reconnectAttempt > 1) { - new SQLReconnectTask().runTaskLater(mcMMO.p, 5); - } - nextReconnectTimestamp = 0; - reconnectAttempt = 0; - return true; - } - } - catch (SQLException e) { - // Failed to check isClosed, so presume connection is bad and attempt later - e.printStackTrace(); - printErrors(e); - } - - reconnectAttempt++; - nextReconnectTimestamp = (long) (System.nanoTime() + Math.min(MAX_WAIT, (reconnectAttempt * SCALING_FACTOR * MIN_WAIT))); - return false; - } - public List getStoredUsers() { ArrayList users = new ArrayList(); - if (checkConnected()) { - Statement stmt = null; - try { - stmt = connection.createStatement(); - ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users"); - while (result.next()) { - users.add(result.getString("user")); + Statement stmt = null; + Connection connection = null; + + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); + stmt = connection.createStatement(); + ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users"); + while (result.next()) { + users.add(result.getString("user")); + } + result.close(); + } catch (SQLException e) { + printErrors(e); + } finally { + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + // Ignore } - result.close(); } - catch (SQLException e) { - printErrors(e); - } - finally { - if (stmt != null) { - try { - stmt.close(); - } - catch (SQLException e) { - // Ignore - } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + // Ignore } } } @@ -801,50 +727,10 @@ public final class SQLDatabaseManager implements DatabaseManager { return users; } - /** - * Attempt to connect to the mySQL database. - */ - private void connect() { - connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName(); - - try { - mcMMO.p.getLogger().info("Attempting connection to MySQL..."); - - // Force driver to load if not yet loaded - Class.forName("com.mysql.jdbc.Driver"); - Properties connectionProperties = new Properties(); - connectionProperties.put("user", Config.getInstance().getMySQLUserName()); - connectionProperties.put("password", Config.getInstance().getMySQLUserPassword()); - connectionProperties.put("autoReconnect", "false"); - connection = DriverManager.getConnection(connectionString, connectionProperties); - - mcMMO.p.getLogger().info("Connection to MySQL was a success!"); - } - catch (SQLException ex) { - connection = null; - - if (reconnectAttempt == 0 || reconnectAttempt >= 11) { - mcMMO.p.getLogger().severe("Connection to MySQL failed!"); - printErrors(ex); - } - } - catch (ClassNotFoundException ex) { - connection = null; - - if (reconnectAttempt == 0 || reconnectAttempt >= 11) { - mcMMO.p.getLogger().severe("MySQL database driver not found!"); - } - } - } - /** * Checks that the database structure is present and correct */ private void checkStructure() { - if (!checkConnected()) { - return; - } - write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` (" + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT," + "`user` varchar(40) NOT NULL," @@ -921,14 +807,11 @@ public final class SQLDatabaseManager implements DatabaseManager { /** * Check database structure for necessary upgrades. - * - * @param upgrade Upgrade to attempt to apply + * + * @param upgrade + * Upgrade to attempt to apply */ private void checkDatabaseStructure(UpgradeType upgrade) { - if (!checkConnected()) { - return; - } - if (!mcMMO.getUpgradeManager().shouldUpgrade(upgrade)) { mcMMO.p.debug("Skipping " + upgrade.name() + " upgrade (unneeded)"); return; @@ -936,7 +819,10 @@ public final class SQLDatabaseManager implements DatabaseManager { Statement statement = null; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.createStatement(); switch (upgrade) { @@ -978,16 +864,20 @@ public final class SQLDatabaseManager implements DatabaseManager { } mcMMO.getUpgradeManager().setUpgradeCompleted(upgrade); - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -996,34 +886,38 @@ public final class SQLDatabaseManager implements DatabaseManager { /** * Attempt to write the SQL query. - * - * @param sql Query to write. - * + * + * @param sql + * Query to write. + * * @return true if the query was successfully written, false otherwise. */ private boolean write(String sql) { - if (!checkConnected()) { - return false; - } - PreparedStatement statement = null; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement(sql); statement.executeUpdate(); return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { if (!sql.contains("DROP COLUMN")) { printErrors(ex); } return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -1032,32 +926,37 @@ public final class SQLDatabaseManager implements DatabaseManager { /** * Returns the number of rows affected by either a DELETE or UPDATE query - * - * @param sql SQL query to execute - * + * + * @param sql + * SQL query to execute + * * @return the number of rows affected */ private int update(String sql) { int rows = 0; - if (checkConnected()) { - PreparedStatement statement = null; + PreparedStatement statement = null; + Connection connection = null; - try { - statement = connection.prepareStatement(sql); - rows = statement.executeUpdate(); + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); + statement = connection.prepareStatement(sql); + rows = statement.executeUpdate(); + } catch (SQLException ex) { + printErrors(ex); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + // Ignore + } } - catch (SQLException ex) { - printErrors(ex); - } - finally { - if (statement != null) { - try { - statement.close(); - } - catch (SQLException e) { - // Ignore - } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + // Ignore } } } @@ -1067,43 +966,49 @@ public final class SQLDatabaseManager implements DatabaseManager { /** * Read SQL query. - * - * @param sql SQL query to read - * + * + * @param sql + * SQL query to read + * * @return the rows in this SQL query */ private HashMap> read(String sql) { HashMap> rows = new HashMap>(); - if (checkConnected()) { - PreparedStatement statement = null; - ResultSet resultSet; + PreparedStatement statement = null; + ResultSet resultSet; - try { - statement = connection.prepareStatement(sql); - resultSet = statement.executeQuery(); + Connection connection = null; - while (resultSet.next()) { - ArrayList column = new ArrayList(); + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); + statement = connection.prepareStatement(sql); + resultSet = statement.executeQuery(); - for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { - column.add(resultSet.getString(i)); - } + while (resultSet.next()) { + ArrayList column = new ArrayList(); - rows.put(resultSet.getRow(), column); + for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { + column.add(resultSet.getString(i)); + } + + rows.put(resultSet.getRow(), column); + } + } catch (SQLException ex) { + printErrors(ex); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + // Ignore } } - catch (SQLException ex) { - printErrors(ex); - } - finally { - if (statement != null) { - try { - statement.close(); - } - catch (SQLException e) { - // Ignore - } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + // Ignore } } } @@ -1113,35 +1018,31 @@ public final class SQLDatabaseManager implements DatabaseManager { /** * Get the Integer. Only return first row / first field. - * - * @param statement SQL query to execute - * + * + * @param statement + * SQL query to execute + * * @return the value in the first row / first field */ private int readInt(PreparedStatement statement) { int result = -1; - if (checkConnected()) { - ResultSet resultSet; + ResultSet resultSet; - try { - resultSet = statement.executeQuery(); + try { + resultSet = statement.executeQuery(); - if (resultSet.next()) { - result = resultSet.getInt(1); - } + if (resultSet.next()) { + result = resultSet.getInt(1); } - catch (SQLException ex) { - printErrors(ex); - } - finally { - if (statement != null) { - try { - statement.close(); - } - catch (SQLException e) { - // Ignore - } + } catch (SQLException ex) { + printErrors(ex); + } finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + // Ignore } } } @@ -1152,7 +1053,10 @@ public final class SQLDatabaseManager implements DatabaseManager { private void writeMissingRows(int id) { PreparedStatement statement = null; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement("INSERT IGNORE INTO " + tablePrefix + "experience (user_id) VALUES (?)"); statement.setInt(1, id); statement.execute(); @@ -1172,16 +1076,20 @@ public final class SQLDatabaseManager implements DatabaseManager { statement.setInt(1, id); statement.execute(); statement.close(); - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -1196,8 +1104,10 @@ public final class SQLDatabaseManager implements DatabaseManager { private boolean saveIntegers(String sql, int... args) { PreparedStatement statement = null; + Connection connection = null; try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement(sql); int i = 1; @@ -1207,17 +1117,21 @@ public final class SQLDatabaseManager implements DatabaseManager { statement.execute(); return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -1227,7 +1141,10 @@ public final class SQLDatabaseManager implements DatabaseManager { private boolean saveLongs(String sql, int id, long... args) { PreparedStatement statement = null; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement(sql); int i = 1; @@ -1238,17 +1155,21 @@ public final class SQLDatabaseManager implements DatabaseManager { statement.setInt(i++, id); statement.execute(); return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -1257,21 +1178,32 @@ public final class SQLDatabaseManager implements DatabaseManager { /** * Retrieve the database id for a player - * - * @param playerName The name of the user to retrieve the id for - * + * + * @param playerName + * The name of the user to retrieve the id for + * * @return the requested id or -1 if not found */ private int readId(String playerName) { int id = -1; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); PreparedStatement statement = connection.prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?"); statement.setString(1, playerName); id = readInt(statement); - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + // Ignore + } + } } return id; @@ -1280,23 +1212,30 @@ public final class SQLDatabaseManager implements DatabaseManager { private boolean saveUniqueId(int id, String uuid) { PreparedStatement statement = null; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET uuid = ? WHERE id = ?"); statement.setString(1, uuid); statement.setInt(2, id); statement.execute(); return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -1306,23 +1245,30 @@ public final class SQLDatabaseManager implements DatabaseManager { private boolean saveLogin(int id, long login) { PreparedStatement statement = null; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement("UPDATE " + tablePrefix + "users SET lastlogin = ? WHERE id = ?"); statement.setLong(1, login); statement.setInt(2, id); statement.execute(); return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -1332,23 +1278,30 @@ public final class SQLDatabaseManager implements DatabaseManager { private boolean saveHuds(int userId, String mobHealthBar) { PreparedStatement statement = null; + Connection connection = null; + try { + connection = connectionPool.getConnection(VALID_TIMEOUT); statement = connection.prepareStatement("UPDATE " + tablePrefix + "huds SET mobhealthbar = ? WHERE user_id = ?"); statement.setString(1, mobHealthBar); statement.setInt(2, userId); statement.execute(); return true; - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); return false; - } - finally { + } finally { if (statement != null) { try { statement.close(); + } catch (SQLException e) { + // Ignore } - catch (SQLException e) { + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { // Ignore } } @@ -1356,13 +1309,20 @@ public final class SQLDatabaseManager implements DatabaseManager { } private PlayerProfile loadFromResult(String playerName, ResultSet result) throws SQLException { - Map skills = new HashMap(); // Skill & Level - Map skillsXp = new HashMap(); // Skill & XP - Map skillsDATS = new HashMap(); // Ability & Cooldown + Map skills = new HashMap(); // Skill + // & + // Level + Map skillsXp = new HashMap(); // Skill + // & + // XP + Map skillsDATS = new HashMap(); // Ability + // & + // Cooldown MobHealthbarType mobHealthbarType; UUID uuid; - final int OFFSET_SKILLS = 0; // TODO update these numbers when the query changes (a new skill is added) + final int OFFSET_SKILLS = 0; // TODO update these numbers when the query + // changes (a new skill is added) final int OFFSET_XP = 13; final int OFFSET_DATS = 26; final int OFFSET_OTHER = 38; @@ -1410,15 +1370,13 @@ public final class SQLDatabaseManager implements DatabaseManager { try { mobHealthbarType = MobHealthbarType.valueOf(result.getString(OFFSET_OTHER + 2)); - } - catch (Exception e) { + } catch (Exception e) { mobHealthbarType = Config.getInstance().getMobHealthbarDefault(); } try { uuid = UUID.fromString(result.getString(OFFSET_OTHER + 3)); - } - catch (Exception e) { + } catch (Exception e) { uuid = null; } @@ -1438,8 +1396,7 @@ public final class SQLDatabaseManager implements DatabaseManager { private void checkUpgradeAddAlchemy(final Statement statement) throws SQLException { try { statement.executeQuery("SELECT `alchemy` FROM `" + tablePrefix + "skills` LIMIT 1"); - } - catch (SQLException ex) { + } catch (SQLException ex) { mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy..."); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD `alchemy` int(10) NOT NULL DEFAULT '0'"); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "experience` ADD `alchemy` int(10) NOT NULL DEFAULT '0'"); @@ -1449,8 +1406,7 @@ public final class SQLDatabaseManager implements DatabaseManager { private void checkUpgradeAddBlastMiningCooldown(final Statement statement) throws SQLException { try { statement.executeQuery("SELECT `blast_mining` FROM `" + tablePrefix + "cooldowns` LIMIT 1"); - } - catch (SQLException ex) { + } catch (SQLException ex) { mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Blast Mining..."); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "cooldowns` ADD `blast_mining` int(32) NOT NULL DEFAULT '0'"); } @@ -1459,8 +1415,7 @@ public final class SQLDatabaseManager implements DatabaseManager { private void checkUpgradeAddFishing(final Statement statement) throws SQLException { try { statement.executeQuery("SELECT `fishing` FROM `" + tablePrefix + "skills` LIMIT 1"); - } - catch (SQLException ex) { + } catch (SQLException ex) { mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Fishing..."); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD `fishing` int(10) NOT NULL DEFAULT '0'"); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "experience` ADD `fishing` int(10) NOT NULL DEFAULT '0'"); @@ -1470,8 +1425,7 @@ public final class SQLDatabaseManager implements DatabaseManager { private void checkUpgradeAddMobHealthbars(final Statement statement) throws SQLException { try { statement.executeQuery("SELECT `mobhealthbar` FROM `" + tablePrefix + "huds` LIMIT 1"); - } - catch (SQLException ex) { + } catch (SQLException ex) { mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for mob healthbars..."); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "huds` ADD `mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "'"); } @@ -1492,22 +1446,18 @@ public final class SQLDatabaseManager implements DatabaseManager { try { statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD INDEX `idx_" + skill_name + "` (`" + skill_name + "`) USING BTREE"); - } - catch (SQLException ex) { + } catch (SQLException ex) { // Ignore } } } - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (resultSet != null) { try { resultSet.close(); - } - catch (SQLException e) { + } catch (SQLException e) { // Ignore } } @@ -1535,16 +1485,13 @@ public final class SQLDatabaseManager implements DatabaseManager { mcMMO.p.getLogger().info("Adding UUIDs to mcMMO MySQL user table..."); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` ADD `uuid` varchar(36) NOT NULL DEFAULT ''"); } - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (resultSet != null) { try { resultSet.close(); - } - catch (SQLException e) { + } catch (SQLException e) { // Ignore } } @@ -1556,16 +1503,13 @@ public final class SQLDatabaseManager implements DatabaseManager { while (resultSet.next()) { names.add(resultSet.getString("user")); } - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (resultSet != null) { try { resultSet.close(); - } - catch (SQLException e) { + } catch (SQLException e) { // Ignore } } @@ -1596,16 +1540,13 @@ public final class SQLDatabaseManager implements DatabaseManager { mcMMO.p.getLogger().info("Removing party name from users table..."); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "users` DROP COLUMN `party`"); } - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (resultSet != null) { try { resultSet.close(); - } - catch (SQLException e) { + } catch (SQLException e) { // Ignore } } @@ -1632,16 +1573,13 @@ public final class SQLDatabaseManager implements DatabaseManager { mcMMO.p.getLogger().info("Removing Spout HUD type from huds table..."); statement.executeUpdate("ALTER TABLE `" + tablePrefix + "huds` DROP COLUMN `hudtype`"); } - } - catch (SQLException ex) { + } catch (SQLException ex) { printErrors(ex); - } - finally { + } finally { if (resultSet != null) { try { resultSet.close(); - } - catch (SQLException e) { + } catch (SQLException e) { // Ignore } }