package me.desht.chesscraft.results; import me.desht.chesscraft.ChessCraft; import me.desht.chesscraft.DirectoryStructure; import me.desht.chesscraft.exceptions.ChessException; import me.desht.dhutils.Debugger; import me.desht.dhutils.LogUtils; import org.bukkit.Bukkit; import org.bukkit.configuration.Configuration; import java.io.File; import java.sql.*; import java.util.ArrayList; import java.util.List; public class ResultsDB { private SupportedDrivers activeDriver; enum SupportedDrivers { MYSQL, SQLITE } private Connection connection; ResultsDB() throws ClassNotFoundException, SQLException { makeDBConnection(); } void shutdown() { try { if (!connection.getAutoCommit()) { connection.rollback(); } Debugger.getInstance().debug("Closing DB connection to " + connection.getMetaData().getDatabaseProductName()); connection.close(); } catch (SQLException e) { LogUtils.warning("can't cleanly shut down DB connection: " + e.getMessage()); } } Connection getConnection() { return connection; } void makeDBConnection() throws SQLException, ClassNotFoundException { connection = null; String dbType = ChessCraft.getInstance().getConfig().getString("database.driver", "sqlite"); SupportedDrivers driver = SupportedDrivers.valueOf(dbType.toUpperCase()); switch (driver) { case MYSQL: connection = connectMySQL(); setupTablesMySQL(); break; case SQLITE: connection = connectSQLite(); setupTablesSQLite(); break; default: throw new ChessException("unsupported database type: " + dbType); } this.activeDriver = driver; setupTablesCommon(); checkForOldFormatData(); Debugger.getInstance().debug("Connected to DB: " + connection.getMetaData().getDatabaseProductName()); } public SupportedDrivers getActiveDriver() { return activeDriver; } private void checkForOldFormatData() { File oldDbFile = new File(DirectoryStructure.getResultsDir(), "results.db"); if (!oldDbFile.exists()) { return; } try { LogUtils.info("Migrating old-format game results into new DB schema..."); Class.forName("org.sqlite.JDBC"); Connection oldConn = DriverManager.getConnection("jdbc:sqlite:" + oldDbFile.getAbsolutePath()); Statement st = oldConn.createStatement(); ResultSet rs = st.executeQuery("select * from " + Results.getResultsHandler().getTableName("results")); List<ResultEntry> entries = new ArrayList<ResultEntry>(); while (rs.next()) { ResultEntry e = new ResultEntry(rs); entries.add(e); } oldConn.close(); connection.setAutoCommit(false); for (ResultEntry re : entries) { re.saveToDatabase(connection); } connection.setAutoCommit(true); LogUtils.info("Sucessfully migrated " + entries.size() + " old-format game results"); File oldDbBackup = new File(DirectoryStructure.getResultsDir(), "oldresults.db"); if (!oldDbFile.renameTo(oldDbBackup)) { LogUtils.warning("couldn't rename " + oldDbFile + " to" + oldDbBackup); } Bukkit.getScheduler().runTask(ChessCraft.getInstance(), new Runnable() { @Override public void run() { Results.getResultsHandler().rebuildViews(); } }); } catch (Exception e) { LogUtils.warning("Could not migrate old-format game results: " + e.getMessage()); } } private Connection connectSQLite() throws ClassNotFoundException, SQLException { Class.forName("org.sqlite.JDBC"); File dbFile = new File(DirectoryStructure.getResultsDir(), "gameresults.db"); return DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath()); } private Connection connectMySQL() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.jdbc.Driver"); Configuration config = ChessCraft.getInstance().getConfig(); String user = config.getString("database.user", "chesscraft"); String pass = config.getString("database.password", ""); String host = config.getString("database.host", "localhost"); String dbName = config.getString("database.name", "chesscraft"); int port = config.getInt("database.port", 3306); String url = "jdbc:mysql://" + host + ":" + port + "/" + dbName; return DriverManager.getConnection(url, user, pass); } private void setupTablesSQLite() throws SQLException { createTableIfNotExists("results", "gameID INTEGER PRIMARY KEY," + "playerWhite VARCHAR(32) NOT NULL," + "playerBlack VARCHAR(32) NOT NULL," + "gameName VARCHAR(64) NOT NULL," + "startTime DATETIME NOT NULL," + "endTime DATETIME NOT NULL," + "result TEXT NOT NULL," + "pgnResult TEXT NOT NULL"); } private void setupTablesMySQL() throws SQLException { createTableIfNotExists("results", "gameID INTEGER NOT NULL AUTO_INCREMENT," + "playerWhite VARCHAR(32) NOT NULL," + "playerBlack VARCHAR(32) NOT NULL," + "gameName VARCHAR(64) NOT NULL," + "startTime DATETIME NOT NULL," + "endTime DATETIME NOT NULL," + "result TEXT NOT NULL," + "pgnResult TEXT NOT NULL," + "PRIMARY KEY (gameID)"); } private void setupTablesCommon() throws SQLException { createTableIfNotExists("ladder", "player VARCHAR(32) NOT NULL," + "score INTEGER NOT NULL," + "PRIMARY KEY (player)"); createTableIfNotExists("league", "player VARCHAR(32) NOT NULL," + "score INTEGER NOT NULL," + "PRIMARY KEY (player)"); String resultsTable = ChessCraft.getInstance().getConfig().getString("database.table_prefix", "chesscraft_") + "results"; createTableIfNotExists("pgn", "gameID INTEGER NOT NULL," + "pgnData TEXT NOT NULL," + "FOREIGN KEY (gameID) REFERENCES " + resultsTable + "(gameID) ON DELETE CASCADE"); } private void createTableIfNotExists(String tableName, String ddl) throws SQLException { String fullName = ChessCraft.getInstance().getConfig().getString("database.table_prefix", "chesscraft_") + tableName; Statement stmt = connection.createStatement(); try { if (tableExists(tableName)) { stmt.executeUpdate("ALTER TABLE " + tableName + " RENAME TO " + fullName); LogUtils.info("renamed DB table " + tableName + " to " + fullName); } else if (!tableExists(fullName)) { stmt.executeUpdate("CREATE TABLE " + fullName + "(" + ddl + ")"); } } catch (SQLException e) { LogUtils.warning("can't execute " + stmt + ": " + e.getMessage()); throw e; } } private boolean tableExists(String table) throws SQLException { DatabaseMetaData dbm = connection.getMetaData(); ResultSet tables = dbm.getTables(null , null, table, null); return tables.next(); } }