package be.jeffcheasey88.peeratcode.repository; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import com.password4j.Hash; import com.password4j.Password; import be.jeffcheasey88.peeratcode.Configuration; import be.jeffcheasey88.peeratcode.model.Badge; import be.jeffcheasey88.peeratcode.model.Chapter; import be.jeffcheasey88.peeratcode.model.Completion; import be.jeffcheasey88.peeratcode.model.Group; import be.jeffcheasey88.peeratcode.model.PeerAtUser; import be.jeffcheasey88.peeratcode.model.Player; import be.jeffcheasey88.peeratcode.model.Puzzle; public class DatabaseRepository { private Connection con; private Configuration config; public DatabaseRepository(Configuration config) { this.config = config; } // testTrigger(); // } // // private void testTrigger(){ // try { // ensureConnection(); // }catch(Exception e){ // e.printStackTrace(); // } // System.out.println("connection ensured"); // // try { // PreparedStatement log = this.con.prepareStatement("DROP TABLE mycustomlog;"); // log.execute(); // }catch(Exception e){ // e.printStackTrace(); // } // System.out.println("log dropped"); // // try { // PreparedStatement log = this.con.prepareStatement("CREATE TABLE mycustomlog(\r\n" // + " message VARCHAR(255),\r\n" // + " primary key (message)\r\n" // + ");"); // log.execute(); // }catch(Exception e){ // e.printStackTrace(); // } // System.out.println("log created"); // // try { // System.out.println(DatabaseQuery.FIRST_TRY.toString()); // DatabaseQuery.FIRST_TRY.prepare(this.con).execute(); // }catch(Exception e){ // e.printStackTrace(); // } // // System.out.println("trigger inserted"); // // try { // insertCompletion(new Completion(1, 1, 1, null, 1)); // } catch (SQLException e1) { // e1.printStackTrace(); // } // // try { // showLog(); // } catch (Exception e) { // e.printStackTrace(); // } // System.out.println("------------------------------"); // } // // private void showLog() throws Exception{ // ensureConnection(); // // PreparedStatement stmt = this.con.prepareStatement("SELECT * FROM mycustomlog"); // ResultSet result = stmt.executeQuery(); // while(result.next()){ // System.out.println("[LOG] "+result.getString("message")); // } // } private void ensureConnection() throws SQLException { if (con == null || (!con.isValid(5))) { this.con = DriverManager.getConnection( "jdbc:mysql://" + config.getDbHost() + ":" + config.getDbPort() + "/" + config.getDbDatabase() + "", config.getDbUser(), config.getDbPassword()); } } private Puzzle makePuzzle(ResultSet puzzleResult) throws SQLException { return new Puzzle(puzzleResult.getInt("id_puzzle"), puzzleResult.getString("name"), puzzleResult.getString("content"), puzzleResult.getBytes("soluce"), puzzleResult.getString("verify"), puzzleResult.getInt("score_max"), puzzleResult.getString("tags"), hasColumn(puzzleResult, "origin") ? puzzleResult.getInt("origin") : -1); } private Chapter makeChapter(ResultSet chapterResult) throws SQLException { return new Chapter(chapterResult.getInt("id_chapter"), chapterResult.getString("name"), chapterResult.getTimestamp("start_date"), chapterResult.getTimestamp("end_date")); } private Completion makeCompletion(ResultSet completionResult) throws SQLException { String fileName = null; if (hasColumn(completionResult, "fileName")) fileName = completionResult.getString("fileName"); String puzzleName = null; if (hasColumn(completionResult, "name")) puzzleName = completionResult.getString("name"); return new Completion(completionResult.getInt("fk_player"), completionResult.getInt("fk_puzzle"), completionResult.getInt("tries"), fileName, completionResult.getInt("score"), puzzleName); } private Player makePlayer(ResultSet playerResult, int id) throws SQLException { Player p = new Player(playerResult.getString("pseudo"), playerResult.getString("email"), playerResult.getString("firstName"), playerResult.getString("lastName"), playerResult.getString("description")); if (hasColumn(playerResult, "avatar")) { p.setAvatar(playerResult.getBytes("avatar")); } if (hasColumn(playerResult, "score")) { p.addCompletion(new Completion(playerResult.getInt("tries"), playerResult.getInt("score"))); for (int ct = 1; ct < playerResult.getInt("completions"); ct++) { // TODO refactor for V3 p.addCompletion(new Completion(0, 0)); } } // Manage groups String groupName = playerResult.getString("name"); if (groupName != null) { p.addGroup(makeGroup(playerResult)); } // ADD rank PreparedStatement completionsStmt = DatabaseQuery.GET_PLAYER_RANK.prepare(con); completionsStmt.setInt(1, id); ResultSet result = completionsStmt.executeQuery(); while (result.next()) { p.setRank(result.getInt("rank")); } return p; } private Group makeGroup(ResultSet result) throws SQLException { return new Group(result.getString("name"), result.getInt("fk_chapter"), result.getInt("fk_puzzle")); } private Player makeGroupPlayer(ResultSet result) throws SQLException { return new Player(result.getString("pseudo"), result.getInt("score"), result.getInt("tries")); } private Badge makeBadge(ResultSet rs) throws SQLException { return new Badge(rs.getString("name"), rs.getBytes("logo"), rs.getInt("level")); } private boolean hasColumn(ResultSet rs, String columnName) throws SQLException { // Found on StackOverflow ResultSetMetaData rsmd = rs.getMetaData(); int columns = rsmd.getColumnCount(); for (int x = 1; x <= columns; x++) { if (columnName.equals(rsmd.getColumnName(x))) return true; } return false; } private List getPuzzlesInChapter(int id) throws SQLException { List puzzles = new ArrayList<>(); ensureConnection(); PreparedStatement puzzleStmt = DatabaseQuery.PUZZLES_IN_CHAPTER_QUERY.prepare(this.con); puzzleStmt.setInt(1, id); ResultSet puzzleResult = puzzleStmt.executeQuery(); while (puzzleResult.next()) { puzzles.add(makePuzzle(puzzleResult)); } return puzzles; } /** * Get a specific puzzle * * @param id The id of the puzzle * @return The puzzle or null if an error occurred */ public Puzzle getPuzzle(int id) { try { ensureConnection(); PreparedStatement puzzleStmt = DatabaseQuery.SPECIFIC_PUZZLE_QUERY.prepare(this.con); puzzleStmt.setInt(1, id); ResultSet puzzleResult = puzzleStmt.executeQuery(); if (puzzleResult.next()) { return makePuzzle(puzzleResult); } } catch (SQLException e) { e.printStackTrace(); } return null; } public int getScore(int user, int puzzle) { try { ensureConnection(); PreparedStatement stmt = DatabaseQuery.SCORE_GROUP.prepare(this.con); stmt.setInt(1, user); stmt.setInt(2, puzzle); ResultSet result = stmt.executeQuery(); if (result.next()) return result.getInt("score"); stmt = DatabaseQuery.SCORE.prepare(this.con); stmt.setInt(1, user); stmt.setInt(2, puzzle); result = stmt.executeQuery(); if (result.next()) return result.getInt("score"); } catch (Exception e) { e.printStackTrace(); } return -1; } public Completion getCompletionGroup(int user, int puzzle) { try { PreparedStatement stmt = DatabaseQuery.GET_COMPLETION_GROUP.prepare(this.con); stmt.setInt(1, user); stmt.setInt(2, puzzle); ResultSet result = stmt.executeQuery(); if (result.next()) return makeCompletion(result); } catch (SQLException e) { e.printStackTrace(); } return getCompletion(user, puzzle); } public Completion getCompletion(int playerId, int puzzleId) { try { PreparedStatement completionsStmt = DatabaseQuery.GET_COMPLETION.prepare(this.con); completionsStmt.setInt(1, puzzleId); completionsStmt.setInt(2, playerId); ResultSet result = completionsStmt.executeQuery(); if (result.next()) { return makeCompletion(result); } } catch (SQLException e) { e.printStackTrace(); } return null; } public Player getPlayer(int idPlayer) { try { PreparedStatement completionsStmt = DatabaseQuery.GET_PLAYER_SIMPLE.prepare(this.con); completionsStmt.setInt(1, idPlayer); ResultSet result = completionsStmt.executeQuery(); if (result.next()) { return makePlayer(result, idPlayer); } } catch (SQLException e) { e.printStackTrace(); } return null; } public Player getPlayerDetails(int idPlayer) { return getPlayerDetails(idPlayer, null); } public Player getPlayerDetails(String pseudoPlayer) { return getPlayerDetails(-1, pseudoPlayer); } private Player getPlayerDetails(int id, String pseudo) { try { ensureConnection(); PreparedStatement completionsStmt; if (pseudo != null) { completionsStmt = DatabaseQuery.GET_PLAYER_DETAILS_BY_PSEUDO.prepare(this.con); completionsStmt.setString(1, pseudo); } else { completionsStmt = DatabaseQuery.GET_PLAYER_DETAILS_BY_ID.prepare(this.con); completionsStmt.setInt(1, id); } ResultSet result = completionsStmt.executeQuery(); Player player = null; while (result.next()) { if (player == null) { id = result.getInt("id_player"); player = makePlayer(result, id); completionsStmt = DatabaseQuery.GET_BADGES_OF_PLAYER.prepare(this.con); completionsStmt.setInt(1, id); ResultSet resultBadges = completionsStmt.executeQuery(); while (resultBadges.next()) { player.addBadge(makeBadge(resultBadges)); } } else { player.addGroup(makeGroup(result)); } } // ADD completions completionsStmt = DatabaseQuery.GET_PLAYER_COMPLETIONS.prepare(con); completionsStmt.setInt(1, id); result = completionsStmt.executeQuery(); while (result.next()) { player.addCompletion(makeCompletion(result)); } return player; } catch (SQLException e) { e.printStackTrace(); } return null; } public SortedSet getAllPlayerForLeaderboard() { try { ensureConnection(); PreparedStatement playersStmt = DatabaseQuery.ALL_PLAYERS_FOR_LEADERBOARD.prepare(this.con); ResultSet result = playersStmt.executeQuery(); ArrayList players = new ArrayList(); Player tmpPlayer; while (result.next()) { tmpPlayer = makePlayer(result, result.getInt("id_player")); if (!players.contains(tmpPlayer)) { players.add(tmpPlayer); } else { players.get(players.indexOf(tmpPlayer)).addGroup(makeGroup(result)); } } return new TreeSet(players); } catch (SQLException e) { e.printStackTrace(); } return null; } public SortedSet getAllGroupForChapterLeaderboard(int chapterId){ try{ ensureConnection(); PreparedStatement groupsStmt = DatabaseQuery.ALL_GROUP_FOR_CHAPTER_LEADERBOARD.prepare(this.con); groupsStmt.setInt(1, chapterId); ResultSet result = groupsStmt.executeQuery(); List groups = new ArrayList(); Group tmpGroup; while (result.next()) { tmpGroup = makeGroup(result); if (tmpGroup != null) { int gPosition = groups.indexOf(tmpGroup); if (gPosition < 0) { tmpGroup.addPlayer(makeGroupPlayer(result)); groups.add(tmpGroup); } else { groups.get(gPosition).addPlayer(makeGroupPlayer(result)); } } } return new TreeSet(groups); }catch(SQLException e){ e.printStackTrace(); } return null; } public Badge getBadge(int badgeId) { try { ensureConnection(); PreparedStatement completionsStmt = DatabaseQuery.GET_BADGE.prepare(this.con); completionsStmt.setInt(1, badgeId); ResultSet result = completionsStmt.executeQuery(); if (result.next()) { return makeBadge(result); } } catch (SQLException e) { e.printStackTrace(); } return null; } /** * Get a specific chapter * * @param id The id of the chapter * @return The chapter or null if an error occurred */ public Chapter getChapter(int id) { try { ensureConnection(); PreparedStatement chapterStmt = DatabaseQuery.SPECIFIC_CHAPTER_QUERY.prepare(this.con); chapterStmt.setInt(1, id); ResultSet chapterResult = chapterStmt.executeQuery(); if (chapterResult.next()) { Chapter chapter = makeChapter(chapterResult); List puzzles = getPuzzlesInChapter(id); chapter.setPuzzles(puzzles); return chapter; } } catch (SQLException e) { e.printStackTrace(); } return null; } public Chapter getChapter(Puzzle puzzle){ try { ensureConnection(); PreparedStatement chapterStmt = DatabaseQuery.CHAPTER_FROM_PUZZLE.prepare(this.con); chapterStmt.setInt(1, puzzle.getId()); ResultSet chapterResult = chapterStmt.executeQuery(); if (chapterResult.next()) { Chapter chapter = makeChapter(chapterResult); List puzzles = getPuzzlesInChapter(chapter.getId()); chapter.setPuzzles(puzzles); return chapter; } } catch (SQLException e) { e.printStackTrace(); } return null; } /** * Get all chapters in the database * * @return List of all chapters or null if an error occurred */ public List getAllChapters() { try { List chapterList = new ArrayList<>(); ensureConnection(); PreparedStatement chapterStmt = DatabaseQuery.ALL_CHAPTERS_QUERY.prepare(this.con); ResultSet chapterResult = chapterStmt.executeQuery(); while (chapterResult.next()) { Chapter chapter = makeChapter(chapterResult); chapter.setPuzzles(getPuzzlesInChapter(chapter.getId())); chapterList.add(chapter); } return chapterList; } catch (SQLException e) { e.printStackTrace(); } return null; } public List getAllGroups() { try { ensureConnection(); List list = new ArrayList<>(); PreparedStatement stmt = DatabaseQuery.ALL_GROUPS.prepare(this.con); ResultSet groupResult = stmt.executeQuery(); while (groupResult.next()) list.add(makeGroup(groupResult)); return list; } catch (Exception e) { e.printStackTrace(); } return null; } /** * Check if a pseudo is available * * @param pseudo The pseudo to check * @return True if the pseudo is available, false if it's already taken */ public boolean checkPseudoAvailability(String pseudo) { return checkAvailability(pseudo, DatabaseQuery.CHECK_PSEUDO_AVAILABLE_QUERY.toString()); } /** * Check if an email is available * * @param email The email to check * @return True if the email is available, false if it's already taken */ public boolean checkEmailAvailability(String email) { return checkAvailability(email, DatabaseQuery.CHECK_EMAIL_AVAILABLE_QUERY.toString()); } private boolean checkAvailability(String queriedString, String correspondingQuery) { try { ensureConnection(); PreparedStatement statement = con.prepareStatement(correspondingQuery); statement.setString(1, queriedString); ResultSet result = statement.executeQuery(); return !result.next(); } catch (SQLException e) { e.printStackTrace(); } return false; } /** * Register a new user * * @param pseudo The pseudo of the user * @param email The email of the user * @param password The password of the user * @param firstname The firstname of the user * @param lastname The lastname of the user * @param description The description of the user * @param sgroup The group of the user * @param avatar The avatar of the user * @return True if the user was registered, false if an error occurred */ public int register(String pseudo, String email, String password, String firstname, String lastname, String description, String sgroup, String avatar) { Hash hash = Password.hash(password).withArgon2(); try { ensureConnection(); con.setAutoCommit(false); try (PreparedStatement playerStatement = con.prepareStatement(DatabaseQuery.REGISTER_QUERY.toString(), Statement.RETURN_GENERATED_KEYS)) { playerStatement.setString(1, pseudo); playerStatement.setString(2, email); playerStatement.setString(3, hash.getResult()); playerStatement.setString(4, firstname); playerStatement.setString(5, lastname); playerStatement.setString(6, description); playerStatement.setString(7, avatar); if (playerStatement.executeUpdate() == 1) { ResultSet inserted = playerStatement.getGeneratedKeys(); if (inserted.next()) { int newPlayerId = inserted.getInt(1); if (!sgroup.isEmpty()) { try (PreparedStatement containsGroupsStatement = con .prepareStatement(DatabaseQuery.REGISTER_PLAYER_IN_EXISTING_GROUP.toString())) { containsGroupsStatement.setInt(1, newPlayerId); containsGroupsStatement.setString(2, sgroup); containsGroupsStatement.executeUpdate(); } } con.commit(); con.setAutoCommit(true); return newPlayerId; } } } catch (SQLException e) { con.rollback(); con.setAutoCommit(true); e.printStackTrace(); } } catch (SQLException e) { e.printStackTrace(); } return -1; } /** * Login a user * * @param username The username of the user * @param password The password of the user * @return id the id of the user, -1 if not login successefuly */ public int login(String username, String password) { try { ensureConnection(); PreparedStatement statement = con.prepareStatement(DatabaseQuery.CHECK_PASSWORD.toString()); DatabaseQuery.PUZZLES_IN_CHAPTER_QUERY.prepare(this.con); statement.setString(1, username); ResultSet result = statement.executeQuery(); if (result.next()) { String hashedPassword = result.getString("passwd"); if (Password.check(password, hashedPassword).withArgon2()) return result.getInt("id_player"); } } catch (SQLException e) { } return -1; } public Completion insertOrUpdatePuzzleResponse(int puzzleId, int userId, String fileName, byte[] code, byte[] response, Puzzle currentPuzzle){ try { ensureConnection(); Completion completion = getCompletionGroup(userId, puzzleId); if (completion == null){ System.out.println("Completion is null"); completion = new Completion(userId, puzzleId, fileName, code, response, currentPuzzle); insertCompletion(completion); } else { System.out.println(completion); completion.addTry(currentPuzzle, response); int lastUserId = completion.getPlayerId(); completion.updatePlayer(userId); updateCompletion(completion, lastUserId); } return completion; } catch (SQLException e) { e.printStackTrace(); } return null; } private void insertCompletion(Completion newCompletion) throws SQLException { PreparedStatement statement = DatabaseQuery.INSERT_COMPLETION.prepare(this.con); statement.setInt(1, newCompletion.getPuzzleId()); statement.setInt(2, newCompletion.getPlayerId()); statement.setInt(3, newCompletion.getTries()); statement.setBytes(4, newCompletion.getCode()); statement.setString(5, newCompletion.getFileName()); statement.setInt(6, newCompletion.getScore()); statement.executeUpdate(); } public boolean insertGroup(Group group, PeerAtUser creator) throws SQLException { Integer groupId = getGroupId(group); if (groupId == null) ensureConnection(); PreparedStatement statement = DatabaseQuery.INSERT_GROUP.prepare(this.con); statement.setString(1, group.getName()); statement.setObject(2, group.getLinkToChapter()); // statement.setObject(3, group.getLinkToPuzzle()); if (statement.executeUpdate() >= 0) return insertUserInGroup(group, creator); return false; } public Group getPlayerGroup(int user, Integer chapter) throws SQLException { ensureConnection(); PreparedStatement stmt = DatabaseQuery.GET_GROUP_FOR_PLAYER.prepare(this.con); stmt.setInt(1, user); stmt.setObject(2, chapter); // stmt.setObject(3, puzzle); ResultSet result = stmt.executeQuery(); if (result.next()) return makeGroup(result); return null; } public Integer getGroupId(Group group) throws SQLException { ensureConnection(); PreparedStatement stmt = DatabaseQuery.GET_GROUP_ID_BY_DATA.prepare(this.con); stmt.setString(1, group.getName()); stmt.setObject(2, group.getLinkToChapter()); // stmt.setObject(3, group.getLinkToPuzzle()); ResultSet result = stmt.executeQuery(); if (result.next()) return result.getInt("id_group"); return null; } public boolean insertUserInGroup(Group group, PeerAtUser user) throws SQLException { Integer id = getGroupId(group); Group alreadyInGroup = getPlayerGroup(user.getId(), group.getLinkToChapter()); if (id != null && alreadyInGroup == null) { PreparedStatement stmt = DatabaseQuery.INSERT_PLAYER_IN_GROUP.prepare(this.con); stmt.setInt(1, user.getId()); stmt.setInt(2, id); return stmt.executeUpdate() >= 0; } return false; } public boolean leaveGroup(Group group, PeerAtUser user) throws SQLException { Integer id = getGroupId(group); if (id != null) { PreparedStatement stmt = DatabaseQuery.LEAVE_GROUP.prepare(this.con); stmt.setInt(1, user.getId()); stmt.setInt(2, id); return stmt.executeUpdate() >= 0; } return false; } private void updateCompletion(Completion completionToUpdate, int user) throws SQLException{ System.out.println("update "+completionToUpdate); PreparedStatement statement = DatabaseQuery.UPDATE_COMPLETION.prepare(this.con); statement.setInt(1, completionToUpdate.getTries()); statement.setInt(2, completionToUpdate.getScore()); statement.setInt(3, completionToUpdate.getPlayerId()); statement.setInt(4, completionToUpdate.getPuzzleId()); statement.setInt(5, user); statement.executeUpdate(); } }