public static Game createGame(Round round, int counter) { String gameName = String.format("%s_%d", round.getLevel().getTournament().getTournamentName(), counter); Game game = new Game(); game.setGameName(gameName); game.setRound(round); game.setState(GameState.boot_pending); game.setStartTime(round.getStartTime()); game.setLocation(randomLocation(round)); game.setSimStartTime(randomSimStartTime(game.getLocation())); game.setServerQueue(Utils.createQueueName()); game.setVisualizerQueue(Utils.createQueueName()); return game; }
private static String randomSimStartTime(String locationString) { Location location = Location.getLocationByName(locationString); long dateTo = location.getDateTo().getTime(); long dateFrom = location.getDateFrom().getTime(); // Number of msecs in a year divided by 4 double gameLength = (3.1556926 * Math.pow(10, 10)) / 4; // Max amount of time between the fromTime to the toTime to start a game long msLength = (long) gameLength; Date starting = new Date(); if ((dateTo - dateFrom) < msLength) { // Use fromTime in all games in the round as the start time starting.setTime(dateFrom); } else { long end = dateTo - msLength; long startTime = (long) (Math.random() * (end - dateFrom) + dateFrom); starting.setTime(startTime); } return Utils.dateToStringSmall(starting); }
@Entity @Table(name = "games") public class Game implements Serializable { private static Logger log = Utils.getLogger(); private static TournamentProperties properties = TournamentProperties.getProperties(); private Integer gameId = 0; private String gameName; private Round round; private Machine machine = null; private GameState state = GameState.boot_pending; private Date startTime; private Date readyTime; private String serverQueue = ""; private String visualizerQueue = ""; private String location = ""; private String simStartTime; private Integer gameLength = 0; private Integer lastTick = 0; private Integer urgency; private Map<Integer, Agent> agentMap = new HashMap<>(); private Random random = new Random(); public Game() {} public void delete(Session session) { // Delete all agent belonging to this broker for (Agent agent : agentMap.values()) { session.delete(agent); session.flush(); } session.delete(this); } @Transient public String getBrokersInGameString() { StringBuilder result = new StringBuilder(); String prefix = ""; for (Agent agent : agentMap.values()) { result.append(prefix).append(agent.getBroker().getBrokerName()); prefix = ", "; } return result.toString(); } @Transient @SuppressWarnings("unchecked") public String getBrokerIdsInGameString() { List<Agent> agents = new ArrayList(agentMap.values()); Collections.sort(agents, new Utils.agentIdComparator()); StringBuilder result = new StringBuilder(); String prefix = ""; for (Agent agent : agents) { result.append(prefix).append(agent.getBroker().getBrokerId()); prefix = ", "; } return result.toString(); } public void removeBootFile() { String bootLocation = properties.getProperty("bootLocation") + gameId + "-boot.xml"; File f = new File(bootLocation); if (!f.exists()) { return; } if (!f.canWrite()) { log.error("Write protected: " + bootLocation); } if (!f.delete()) { log.error("Failed to delete : " + bootLocation); } } public String startTimeUTC() { return Utils.dateToStringFull(startTime); } public String readyTimeUTC() { return Utils.dateToStringFull(readyTime); } @Transient public int getSize() { return agentMap.size(); } // Computes a random game length as outlined in the game specification public int computeGameLength() { String p = round.getRoundName().toLowerCase().contains("test") ? "test" : "competition"; int minLength = properties.getPropertyInt(p + ".minimumTimeslotCount"); int expLength = properties.getPropertyInt(p + ".expectedTimeslotCount"); if (expLength == minLength) { return minLength; } else { double roll = random.nextDouble(); // compute k = ln(1-roll)/ln(1-p) where p = 1/(exp-min) double k = (Math.log(1.0 - roll) / Math.log(1.0 - 1.0 / (expLength - minLength + 1))); return minLength + (int) Math.floor(k); } } public String jenkinsMachineUrl() { if (machine == null) { return ""; } return String.format( "%scomputer/%s/", properties.getProperty("jenkins.location"), machine.getMachineName()); } public void saveStandings() { try { int bootLength = properties.getPropertyInt("bootLength"); gameLength = MemStore.getGameLengths().get(gameId) - bootLength; lastTick = Integer.parseInt(MemStore.getGameHeartbeats().get(gameId)[0]) - bootLength; } catch (Exception ignored) { } } private static String randomLocation(Round round) { double randLocation = Math.random() * round.getLocationsList().size(); return round.getLocationsList().get((int) Math.floor(randLocation)); } private static String randomSimStartTime(String locationString) { Location location = Location.getLocationByName(locationString); long dateTo = location.getDateTo().getTime(); long dateFrom = location.getDateFrom().getTime(); // Number of msecs in a year divided by 4 double gameLength = (3.1556926 * Math.pow(10, 10)) / 4; // Max amount of time between the fromTime to the toTime to start a game long msLength = (long) gameLength; Date starting = new Date(); if ((dateTo - dateFrom) < msLength) { // Use fromTime in all games in the round as the start time starting.setTime(dateFrom); } else { long end = dateTo - msLength; long startTime = (long) (Math.random() * (end - dateFrom) + dateFrom); starting.setTime(startTime); } return Utils.dateToStringSmall(starting); } public static Game createGame(Round round, int counter) { String gameName = String.format("%s_%d", round.getLevel().getTournament().getTournamentName(), counter); Game game = new Game(); game.setGameName(gameName); game.setRound(round); game.setState(GameState.boot_pending); game.setStartTime(round.getStartTime()); game.setLocation(randomLocation(round)); game.setSimStartTime(randomSimStartTime(game.getLocation())); game.setServerQueue(Utils.createQueueName()); game.setVisualizerQueue(Utils.createQueueName()); return game; } public static Game getGameFromId(int gameId) { Game game = null; Session session = HibernateUtil.getSession(); Transaction transaction = session.beginTransaction(); try { game = getGame(session, gameId); transaction.commit(); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); } finally { session.close(); } return game; } public static Game getGame(Session session, int gameId) { Query query = session.createQuery(Constants.HQL.GET_GAME_BY_ID); query.setInteger("gameId", gameId); return (Game) query.uniqueResult(); } // <editor-fold desc="Collections"> @OneToMany @JoinColumn(name = "gameId") @MapKey(name = "brokerId") public Map<Integer, Agent> getAgentMap() { return agentMap; } public void setAgentMap(Map<Integer, Agent> agentMap) { this.agentMap = agentMap; } @SuppressWarnings("unchecked") public static List<Game> getNotCompleteGamesList() { List<Game> games = new ArrayList<Game>(); Session session = HibernateUtil.getSession(); Transaction transaction = session.beginTransaction(); try { Query query = session.createQuery(Constants.HQL.GET_GAMES_NOT_COMPLETE); games = (List<Game>) query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); transaction.commit(); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); } session.close(); return games; } @SuppressWarnings("unchecked") public static List<Game> getCompleteGamesList() { List<Game> games = new ArrayList<Game>(); Session session = HibernateUtil.getSession(); Transaction transaction = session.beginTransaction(); try { Query query = session.createQuery(Constants.HQL.GET_GAMES_COMPLETE); games = (List<Game>) query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); transaction.commit(); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); } session.close(); return games; } // </editor-fold> // <editor-fold desc="Setter and getters"> @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "gameId", unique = true, nullable = false) public Integer getGameId() { return gameId; } public void setGameId(Integer gameId) { this.gameId = gameId; } @Column(name = "gameName") public String getGameName() { return gameName; } public void setGameName(String gameName) { this.gameName = gameName; } @ManyToOne @JoinColumn(name = "roundId") public Round getRound() { return round; } public void setRound(Round round) { this.round = round; } @ManyToOne @JoinColumn(name = "machineId") public Machine getMachine() { return machine; } public void setMachine(Machine machine) { this.machine = machine; } @Column(name = "state", nullable = false) @Enumerated(EnumType.STRING) public GameState getState() { return state; } public void setState(GameState state) { this.state = state; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "startTime") public Date getStartTime() { return startTime; } public void setStartTime(Date startTime) { this.startTime = startTime; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "readyTime") public Date getReadyTime() { return readyTime; } public void setReadyTime(Date readyTime) { this.readyTime = readyTime; } @Column(name = "visualizerQueue") public String getVisualizerQueue() { return visualizerQueue; } public void setVisualizerQueue(String name) { this.visualizerQueue = name; } @Column(name = "serverQueue") public String getServerQueue() { return serverQueue; } public void setServerQueue(String name) { this.serverQueue = name; } @Column(name = "location") public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } @Column(name = "simStartDate") public String getSimStartTime() { return simStartTime; } public void setSimStartTime(String simStartTime) { this.simStartTime = simStartTime; } @Column(name = "gameLength") public Integer getGameLength() { return gameLength; } public void setGameLength(Integer gameLength) { this.gameLength = gameLength; } @Column(name = "lastTick") public Integer getLastTick() { return lastTick; } public void setLastTick(Integer lastTick) { this.lastTick = lastTick; } @Transient public Integer getUrgency() { return urgency; } public void setUrgency(int urgency) { this.urgency = urgency; } // </editor-fold> // Used by timeline @Override public String toString() { return gameId + " : " + getBrokerIdsInGameString(); } }
public String readyTimeUTC() { return Utils.dateToStringFull(readyTime); }
public String startTimeUTC() { return Utils.dateToStringFull(startTime); }
@WebServlet( description = "REST API for game servers", urlPatterns = {"/serverInterface.jsp"}) public class RestServer extends HttpServlet { private static Logger log = Utils.getLogger(); private static String responseType = "text/plain; charset=UTF-8"; private TournamentProperties properties = TournamentProperties.getProperties(); public RestServer() { super(); } protected synchronized void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String result = handleGET(request); writeResult(response, result); } protected synchronized void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException { String result = handlePUT(request); writeResult(response, result); } protected synchronized void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { String result = handlePOST(request); writeResult(response, result); } private void writeResult(HttpServletResponse response, String result) throws IOException { response.setContentType(responseType); response.setContentLength(result.length()); PrintWriter out = response.getWriter(); out.print(result); out.flush(); out.close(); } private String handleGET(HttpServletRequest request) { try { String actionString = request.getParameter(Rest.REQ_PARAM_ACTION); if (actionString.equalsIgnoreCase(Rest.REQ_PARAM_STATUS)) { if (!MemStore.checkMachineAllowed(request.getRemoteAddr())) { return "error"; } return handleStatus(request); } else if (actionString.equalsIgnoreCase(Rest.REQ_PARAM_BOOT)) { return serveBoot(getGameId(request)); } else if (actionString.equalsIgnoreCase(Rest.REQ_PARAM_HEARTBEAT)) { if (!MemStore.checkMachineAllowed(request.getRemoteAddr())) { return "error"; } return handleHeartBeat(request); } } catch (Exception ignored) { } return "error"; } /** Handle 'PUT' to serverInterface.jsp, either boot.xml or (Boot|Sim) log */ private String handlePUT(HttpServletRequest request) { if (!MemStore.checkMachineAllowed(request.getRemoteAddr())) { return "error"; } try { String fileName = request.getParameter(Rest.REQ_PARAM_FILENAME); log.info("Received a file " + fileName); String logLoc = fileName.endsWith("boot.xml") ? properties.getProperty("bootLocation") : properties.getProperty("logLocation"); String pathString = logLoc + fileName; // Write to file InputStream is = request.getInputStream(); FileOutputStream fos = new FileOutputStream(pathString); byte buf[] = new byte[1024]; int letti; while ((letti = is.read(buf)) > 0) { fos.write(buf, 0, letti); } is.close(); fos.close(); // If sim-logs received, extract end-of-game standings new ParserSimlog(pathString).run(); // Create softlinks to named versions String gameName = request.getParameter(Rest.REQ_PARAM_GAMENAME); createSoftLinks(fileName, logLoc, gameName); } catch (Exception e) { return "error"; } return "success"; } /** Handle 'POST' to serverInterface.jsp, this is an end-of-game message */ private String handlePOST(HttpServletRequest request) { if (!MemStore.checkMachineAllowed(request.getRemoteAddr())) { return "error"; } try { String actionString = request.getParameter(Rest.REQ_PARAM_ACTION); if (!actionString.equalsIgnoreCase(Rest.REQ_PARAM_GAMERESULTS)) { log.debug("The message didn't have the right action-string!"); return "error"; } int gameId = getGameId(request); if (!(gameId > 0)) { log.debug("The message didn't have a gameId!"); return "error"; } Session session = HibernateUtil.getSession(); Transaction transaction = session.beginTransaction(); try { Game game = (Game) session.get(Game.class, gameId); String standings = request.getParameter(Rest.REQ_PARAM_MESSAGE); return new GameHandler(game).handleStandings(session, standings, true); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); return "error"; } finally { session.close(); } } catch (Exception e) { log.error("Something went wrong with receiving the POST message!"); log.error(e.getMessage()); return "error"; } } private String serveBoot(int gameId) { StringBuilder result = new StringBuilder(); try { // Determine boot-file location String bootLocation = properties.getProperty("bootLocation") + "game-" + gameId + "-boot.xml"; // Read the file FileInputStream fstream = new FileInputStream(bootLocation); DataInputStream in = new DataInputStream(fstream); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String strLine; while ((strLine = br.readLine()) != null) { result.append(strLine).append("\n"); } // Close the streams fstream.close(); in.close(); br.close(); } catch (Exception e) { log.error(e.getMessage()); return "error"; } return result.toString(); } private String handleStatus(HttpServletRequest request) { String statusString = request.getParameter(Rest.REQ_PARAM_STATUS); int gameId = getGameId(request); log.info(String.format("Received %s message from game: %s", statusString, gameId)); Session session = HibernateUtil.getSession(); Transaction transaction = session.beginTransaction(); try { Query query = session.createQuery(Constants.HQL.GET_GAME_BY_ID); query.setInteger("gameId", gameId); Game game = (Game) query.uniqueResult(); if (game == null) { log.warn( String.format( "Trying to set status %s on non-existing " + "game : %s", statusString, gameId)); return "error"; } new GameHandler(game).handleStatus(session, statusString); transaction.commit(); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); return "error"; } finally { session.close(); } String gameLength = request.getParameter(Rest.REQ_PARAM_GAMELENGTH); if (gameLength != null && transaction.wasCommitted()) { log.info(String.format("Received gamelength %s for game %s", gameLength, gameId)); MemStore.addGameLength(gameId, gameLength); } return "success"; } private String handleHeartBeat(HttpServletRequest request) { int gameId; // Write heartbeat + elapsed time to the MemStore try { String message = request.getParameter(Rest.REQ_PARAM_MESSAGE); gameId = getGameId(request); if (!(gameId > 0)) { log.debug("The message didn't have a gameId!"); return "error"; } MemStore.addGameHeartbeat(gameId, message); long elapsedTime = Long.parseLong(request.getParameter(Rest.REQ_PARAM_ELAPSED_TIME)); if (elapsedTime > 0) { MemStore.addElapsedTime(gameId, elapsedTime); } } catch (Exception e) { e.printStackTrace(); return "error"; } // Write heartbeat to the DB Session session = HibernateUtil.getSession(); Transaction transaction = session.beginTransaction(); try { Game game = (Game) session.get(Game.class, gameId); String standings = request.getParameter(Rest.REQ_PARAM_STANDINGS); return new GameHandler(game).handleStandings(session, standings, false); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); return "error"; } finally { session.close(); } } private int getGameId(HttpServletRequest request) { int gameId; try { gameId = Integer.parseInt(request.getParameter(Rest.REQ_PARAM_GAMEID)); } catch (Exception ignored) { String niceName = request.getParameter(Rest.REQ_PARAM_GAMEID); gameId = MemStore.getGameId(niceName); } return gameId; } private void createSoftLinks(String fileName, String logLoc, String gameName) { String linkName; if (fileName.endsWith("boot.xml")) { linkName = String.format("%s%s.boot.xml", logLoc, gameName); } else if (fileName.contains("boot")) { linkName = String.format("%s%s.boot.tar.gz", logLoc, gameName); } else { linkName = String.format("%s%s.sim.tar.gz", logLoc, gameName); } try { Path link = Paths.get(linkName); Path target = Paths.get(fileName); Files.createSymbolicLink(link, target); } catch (FileAlreadyExistsException faee) { // Ignored } catch (IOException | UnsupportedOperationException e) { e.printStackTrace(); } } }