/** Profile info operation. */ private long profInfoOp( final HttpServletRequest request, final HttpServletResponse response, final PersistenceManager pm, final ApiAccount apiAccount) throws IOException { LOGGER.fine("API account: " + apiAccount.getUser().getEmail()); final Integer bnetId; final Integer bnetSubId; final String gatewayString; final String playerName; final Gateway gateway; final String bnetProfileUrlParam = request.getParameter(PARAM_BNET_PROFILE_URL); if (bnetProfileUrlParam == null || bnetProfileUrlParam.isEmpty()) { // Player id is provided explicitly bnetId = getIntParam(request, PARAM_BNET_ID); bnetSubId = getIntParam(request, PARAM_BNET_SUBID); gatewayString = request.getParameter(PARAM_GATEWAY); playerName = request.getParameter(PARAM_PLAYER_NAME); if (bnetId == null || bnetSubId == null || gatewayString == null || gatewayString.isEmpty() || playerName == null || playerName.isEmpty()) { LOGGER.warning("Missing parameters!"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing parameters!"); return 0; } gateway = Gateway.fromBinaryValue(gatewayString); if (gateway == Gateway.UNKNOWN || gateway == Gateway.PUBLIC_TEST) { LOGGER.warning("Invalid gateway parameter: " + gatewayString); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid gateway parameter!"); return 0; } } else { // Player id is provided through his/her Battle.net profile URL try { Gateway foundGateway = null; for (final Gateway gateway_ : EnumCache.GATEWAYS) if (bnetProfileUrlParam.startsWith(gateway_.bnetUrl)) { foundGateway = gateway_; break; } if (foundGateway == null || foundGateway == Gateway.UNKNOWN || foundGateway == Gateway.PUBLIC_TEST) throw new Exception("No matching gateway!"); gateway = foundGateway; final String[] urlParts = bnetProfileUrlParam.split("/"); if (urlParts.length < 3) throw new Exception("Not enough parts in URL!"); playerName = URLDecoder.decode(urlParts[urlParts.length - 1], "UTF-8"); bnetSubId = Integer.valueOf(urlParts[urlParts.length - 2]); bnetId = Integer.valueOf(urlParts[urlParts.length - 3]); } catch (final Exception e) { LOGGER.log(Level.SEVERE, "Invalid Battle.net profile URL: " + bnetProfileUrlParam, e); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid Battle.net profile URL!"); return 0; } } long opsCharged = 1; final Boolean retrieveExtInfoParam = getBooleanParam(request, PARAM_RETRIEVE_EXT_INFO); // Default or requested values: final boolean retrieveExtInfo = retrieveExtInfoParam == null ? false : retrieveExtInfoParam; try { final PlayerId playerId = new PlayerId(); playerId.battleNetId = bnetId; playerId.battleNetSubId = bnetSubId; playerId.gateway = gateway; playerId.name = playerName; final String bnetProfileUrl = playerId.getBattleNetProfileUrl(BnetLanguage.ENGLISH); LOGGER.fine("Bnet profile URL: " + bnetProfileUrl); final URLFetchService urlFetchService = URLFetchServiceFactory.getURLFetchService(); // Default deadline: 5 seconds... increase it! final HTTPRequest profileRequest = new HTTPRequest(new URL(bnetProfileUrl)); profileRequest .getFetchOptions() .setDeadline(50.0); // 50-sec deadline (leaving 10 seconds to process... final Future<HTTPResponse> profileFetchAsync = urlFetchService.fetchAsync(profileRequest); final Future<HTTPResponse> extProfileFetchAsync; if (retrieveExtInfo) { opsCharged++; // Start retrieving extended profile info in parallel final HTTPRequest extProfileRequest = new HTTPRequest(new URL(bnetProfileUrl + "ladder/leagues")); extProfileRequest .getFetchOptions() .setDeadline(50.0); // 50-sec deadline (leaving 10 seconds to process... extProfileFetchAsync = urlFetchService.fetchAsync(extProfileRequest); } else extProfileFetchAsync = null; final XmlBuilder xb = new XmlBuilder("1.1"); HTTPResponse profileResponse = null; Profile profile = null; Element profInfoElement = null; try { profileResponse = profileFetchAsync.get(); switch (profileResponse.getResponseCode()) { case HttpServletResponse.SC_OK: { final byte[] content = profileResponse.getContent(); if (content.length == 0) throw new Exception("Content length = 0!"); profile = BnetUtils.retrieveProfile(null, new ByteArrayInputStream(content)); if (profile != null) { LOGGER.fine("Parse OK"); xb.createResultElement(ProfInfoResult.OK); opsCharged += 2; profInfoElement = xb.setParentElement(xb.createElement(XTAG_PROFILE_INFO)); // Re-include player id final Element playerElement = xb.createElement(XTAG_PLAYER_ID, XATTR_NAME, playerName); playerElement.setAttribute(XATTR_BNET_ID, bnetId.toString()); playerElement.setAttribute(XATTR_BNET_SUBID, bnetSubId.toString()); playerElement.setAttribute(XATTR_GATEWAY, gateway.toString()); playerElement.setAttribute(XATTR_GW_CODE, gateway.binaryValue); playerElement.setAttribute(XATTR_REGION, playerId.getRegion().toString()); playerElement.setAttribute(XATTR_PROFILE_URL, bnetProfileUrl); final Element portraitElement = xb.createElement(XTAG_PORTRAIT, XATTR_GROUP, profile.portraitGroup); portraitElement.setAttribute(XATTR_ROW, Integer.toString(profile.portraitRow)); portraitElement.setAttribute( XATTR_COLUMN, Integer.toString(profile.portraitColumn)); xb.createElement(XTAG_ACHIEVEMENT_POINTS, profile.achievementPoints); xb.createElement(XTAG_TOTAL_CAREER_GAMES, profile.totalCareerGames); xb.createElement(XTAG_GAMES_THIS_SEASON, profile.gamesThisSeason); xb.createElement(XTAG_TERRAN_WINS, profile.terranWins); xb.createElement(XTAG_ZERG_WINS, profile.zergWins); xb.createElement(XTAG_PROTOSS_WINS, profile.protossWins); final Element highestSoloFlElement = xb.createElement(XTAG_HIGHEST_SOLO_FL, profile.highestSoloFinishLeague); if (profile.highestSoloFinishTimes > 0) highestSoloFlElement.setAttribute( XATTR_TIMES_ACHIEVED, Integer.toString(profile.highestSoloFinishTimes)); final Element highestTeamFlElement = xb.createElement(XTAG_HIGHEST_TEAM_FL, profile.highestTeamFinishLeague); if (profile.highestTeamFinishTimes > 0) highestTeamFlElement.setAttribute( XATTR_TIMES_ACHIEVED, Integer.toString(profile.highestTeamFinishTimes)); break; } else { LOGGER.fine("Parse error!"); xb.createResultElement(ProfInfoResult.PARSING_ERROR); // Parse fails } } case HttpServletResponse.SC_NOT_FOUND: LOGGER.fine("Invalid player!"); xb.createResultElement(ProfInfoResult.INVALID_PLAYER); break; default: // Treat other response HTTP status codes as BNET_ERROR throw new Exception("Response code: " + profileResponse.getResponseCode()); } } catch (final Exception e) { LOGGER.log(Level.SEVERE, "", e); xb.createResultElement(ProfInfoResult.BNET_ERROR); } finally { if (retrieveExtInfo && profile == null) extProfileFetchAsync.cancel(true); } if (retrieveExtInfo && profile != null) { try { profileResponse = extProfileFetchAsync.get(); final byte[] content; if (profileResponse.getResponseCode() == HttpServletResponse.SC_OK && (content = profileResponse.getContent()).length > 0) { profile = BnetUtils.retrieveExtProfile(null, new ByteArrayInputStream(content), profile); if (profile != null) { LOGGER.fine("Parse extended OK"); opsCharged += 2; xb.setParentElement(profInfoElement); final Element allRankGroupsElement = xb.setParentElement(xb.createElement(XTAG_ALL_RANK_GROUPS)); int allRankGroupsCount = 0; for (int bracket = 0; bracket < profile.allRankss.length; bracket++) { final TeamRank[] allRanks = profile.allRankss[bracket]; if (allRanks != null && allRanks.length > 0) { allRankGroupsCount++; xb.setParentElement(allRankGroupsElement); final Element allRankGroupElement = xb.createElement(XTAG_ALL_RANK_GROUP, XATTR_COUNT, allRanks.length); allRankGroupElement.setAttribute( XATTR_FORMAT, (bracket + 1) + "v" + (bracket + 1)); for (int i = 0; i < allRanks.length; i++) { xb.setParentElement(allRankGroupElement); final Element teamRankElement = xb.setParentElement( xb.createElement( XTAG_TEAM_RANK, XATTR_LEAGUE, allRanks[i].league.stringValue)); teamRankElement.setAttribute( XATTR_DIVISION_RANK, Integer.toString(allRanks[i].divisionRank)); // Team members xb.setParentElement( xb.createElement( XTAG_TEAM_MEMBERS, XATTR_COUNT, allRanks[i].teamMembers.length)); for (final String memberName : allRanks[i].teamMembers) xb.createElement(XTAG_TEAM_MEMBER, XATTR_NAME, memberName); } } } allRankGroupsElement.setAttribute(XATTR_COUNT, Integer.toString(allRankGroupsCount)); } else LOGGER.fine("Parse extended error!"); } } catch (final Exception e) { LOGGER.log( Level.SEVERE, "Failed to get extended profile info, we return the basic profile info silently.", e); // Failed to get extended profile info, we return the basic profile info silently } } xb.printDocument(response); return opsCharged; } catch (final Exception e) { LOGGER.log(Level.SEVERE, "", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return 0; } }
/** Parse replay operation. */ private long parseRepOp( final HttpServletRequest request, final HttpServletResponse response, final PersistenceManager pm, final ApiAccount apiAccount) throws IOException { final String fileContent = request.getParameter(PARAM_FILE_CONTENT); final Integer fileLength = getIntParam(request, PARAM_FILE_LENGTH); LOGGER.fine("API account: " + apiAccount.getUser().getEmail() + ", file length: " + fileLength); if (fileContent == null || fileContent.isEmpty() || fileLength == null) { LOGGER.warning("Missing parameters!"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing parameters!"); return 0; } final byte[] decodedFileContent = ServerUtils.decodeBase64String(fileContent); if (decodedFileContent == null) { LOGGER.warning("Invalid Base64 encoded file content!"); response.sendError( HttpServletResponse.SC_BAD_REQUEST, "Invalid Base64 encoded file content!"); return 0; } if (decodedFileContent.length != fileLength) { LOGGER.warning( "Supplied file length does not match decoded file content length: " + fileLength + " != " + decodedFileContent.length); response.sendError( HttpServletResponse.SC_BAD_REQUEST, "Supplied file length does not match decoded file content length!"); return 0; } try { long opsCharged = 1; final Boolean parseMessagesParam = getBooleanParam(request, PARAM_PARSE_MESSAGES); final Boolean parseActionsParam = getBooleanParam(request, PARAM_PARSE_ACTIONS); // Default or requested values: final boolean parseMessages = parseMessagesParam == null ? true : parseMessagesParam; final boolean parseActions = parseActionsParam == null ? true : parseActionsParam; final Set<ReplayContent> contentToExtractSet = EnumSet.copyOf(ReplayFactory.GENERAL_INFO_CONTENT); if (parseMessages) contentToExtractSet.add(ReplayContent.MESSAGE_EVENTS); if (parseActions) { contentToExtractSet.add(ReplayContent.GAME_EVENTS); opsCharged++; } final Replay replay = ReplayFactory.parseReplay( "attachedFile.SC2Replay", new MpqParser(new ByteArrayMpqDataInput(decodedFileContent)), contentToExtractSet); final XmlBuilder xb = new XmlBuilder("1.0"); xb.createResultElement(replay == null ? ParseRepResult.PARSING_ERROR : ParseRepResult.OK); xb.createElement(XTAG_ENGINE_VER, ReplayFactory.getVersion()); if (replay == null) { LOGGER.fine("Replay parsing error!"); xb.printDocument(response); return opsCharged; } final Element repInfoElement = xb.setParentElement(xb.createElement(XTAG_REP_INFO)); xb.createElement(XTAG_VERSION, replay.version); xb.createElement(XTAG_EXPANSION, replay.details.expansion); final Element gameLengthSecElement = xb.createElement( XTAG_GAME_LENGTH, replay.converterGameSpeed.convertToRealTime(replay.gameLengthSec)); gameLengthSecElement.setAttribute(XATTR_UNIT, "sec"); gameLengthSecElement.setAttribute( XATTR_GAME_TIME_VALUE, Integer.toString(replay.gameLengthSec)); xb.createElement(XTAG_GAME_LENGTH, replay.frames).setAttribute(XATTR_UNIT, "frame"); xb.createElement(XTAG_GAME_TYPE, replay.initData.gameType); if (replay.initData.competitive != null) xb.createElement(XTAG_IS_COMPETITIVE, replay.initData.competitive); xb.createElement(XTAG_GAME_SPEED, replay.initData.gameSpeed); xb.createElement(XTAG_FORMAT, replay.initData.format); xb.createElement(XTAG_GATEWAY, replay.initData.gateway); xb.createElement(XTAG_MAP_FILE_NAME, replay.initData.mapFileName); xb.setParentElement(xb.createElement(XTAG_CLIENTS)); final Player[] players = replay.details.players; final String[] arrangedClientNames = replay.initData.getArrangedClientNames(players); xb.getParentElement().setAttribute(XATTR_COUNT, Integer.toString(arrangedClientNames.length)); for (int i = 0; i < arrangedClientNames.length; i++) xb.createElement(XTAG_CLIENT, arrangedClientNames[i]) .setAttribute(XATTR_INDEX, Integer.toString(i)); xb.setParentElement(repInfoElement); xb.createElement(XTAG_MAP_NAME, replay.details.originalMapName); xb.createDateTimeElement(XTAG_SAVE_TIME, new Date(replay.details.saveTime)); xb.createElement( XTAG_SAVE_TIME_ZONE, String.format(Locale.US, "%+.2f", replay.details.saveTimeZone)); final Element playersElement = xb.createElement(XTAG_PLAYERS); xb.setParentElement(playersElement); playersElement.setAttribute(XATTR_COUNT, Integer.toString(players.length)); for (int i = 0; i < players.length; i++) { final Player player = players[i]; xb.setParentElement(playersElement); xb.setParentElement(xb.createElement(XTAG_PLAYER, XATTR_INDEX, Integer.toString(i))); final Element playerElement = xb.createElement(XTAG_PLAYER_ID, XATTR_NAME, player.playerId.name); playerElement.setAttribute(XATTR_BNET_ID, Integer.toString(player.playerId.battleNetId)); playerElement.setAttribute( XATTR_BNET_SUBID, Integer.toString(player.playerId.battleNetSubId)); playerElement.setAttribute(XATTR_GATEWAY, player.playerId.gateway.toString()); playerElement.setAttribute(XATTR_GW_CODE, player.playerId.gateway.binaryValue); playerElement.setAttribute(XATTR_REGION, player.playerId.getRegion().toString()); playerElement.setAttribute( XATTR_PROFILE_URL, player.playerId.getBattleNetProfileUrl(player.playerId.gateway.defaultLanguage)); xb.createElement( XTAG_TEAM, player.team == Player.TEAM_UNKNOWN ? TEXT_UNKNOWN : player.team); xb.createElement(XTAG_RACE, player.race); xb.createElement(XTAG_FINAL_RACE, player.finalRace); xb.createElement(XTAG_LEAGUE, player.getLeague()); xb.createElement(XTAG_SWARM_LEVELS, player.getSwarmLevels()); final Element colorElement = xb.createElement(XTAG_COLOR, XATTR_NAME, player.playerColor); colorElement.setAttribute(XATTR_RED, Integer.toString(player.argbColor[1])); colorElement.setAttribute(XATTR_GREEN, Integer.toString(player.argbColor[2])); colorElement.setAttribute(XATTR_BLUE, Integer.toString(player.argbColor[3])); xb.createElement(XTAG_TYPE, player.type); xb.createElement(XTAG_DIFFICULTY, player.difficulty); xb.createElement(XTAG_HANDICAP, player.handicap); xb.createElement(XTAG_IS_WINNER, player.isWinner == null ? TEXT_UNKNOWN : player.isWinner); if (parseActions) { xb.createElement(XTAG_ACTIONS_COUNT, player.actionsCount); xb.createElement(XTAG_EFFECTIVE_ACTIONS_COUNT, player.effectiveActionsCount); xb.createElement(XTAG_LAST_ACTION_FRAME, player.lastActionFrame); xb.createElement(XTAG_APM, ReplayUtils.calculatePlayerApm(replay, player)) .setAttribute( XATTR_EXCLUDED_ACTIONS_COUNT, Integer.toString(player.excludedActionsCount)); xb.createElement(XTAG_EAPM, ReplayUtils.calculatePlayerEapm(replay, player)) .setAttribute( XATTR_EXCLUDED_ACTIONS_COUNT, Integer.toString(player.excludedEffectiveActionsCount)); Float fvalue; xb.createElement( XTAG_AVG_SPAWNING_RATIO, (fvalue = player.getAverageSpawningRatio()) == null ? TEXT_UNKNOWN : (int) (fvalue * 100)) .setAttribute(XATTR_UNIT, "%"); xb.createElement( XTAG_AVG_INJECTION_GAP, (fvalue = player.getAverageInjectionGap()) == null ? TEXT_UNKNOWN : ReplayUtils.formatFramesDecimal( fvalue.intValue(), replay.converterGameSpeed)) .setAttribute(XATTR_UNIT, "sec"); } } if (parseMessages) { xb.setParentElement(null); // Root element final Element inGameChatElement = xb.createElement(XTAG_IN_GAME_CHAT, XATTR_COUNT, replay.messageEvents.messages.length); inGameChatElement.setAttribute(XATTR_PATTERN, "HH:mm:ss"); xb.setParentElement(inGameChatElement); int ms = 0; for (final Message message : replay.messageEvents.messages) { ms += message.time; final Element messageElement = xb.createElement( message instanceof Text ? XTAG_TEXT : XTAG_PING, XATTR_CLIENT_INDEX, message.client); messageElement.setAttribute(XATTR_CLIENT, arrangedClientNames[message.client]); messageElement.setAttribute( XATTR_TIME, ReplayUtils.formatMs(ms, replay.converterGameSpeed)); if (message instanceof Text) { messageElement.setAttribute(XATTR_VALUE, ((Text) message).text); final byte opCode = ((Text) message).opCode; messageElement.setAttribute( XATTR_TARGET, opCode == MessageEvents.OP_CODE_CHAT_TO_ALL ? "all" : opCode == MessageEvents.OP_CODE_CHAT_TO_ALLIES ? "allies" : opCode == MessageEvents.OP_CODE_CHAT_TO_OBSERVERS ? "observers" : "unknown"); } else if (message instanceof Blink) { messageElement.setAttribute(XATTR_X, ReplayUtils.formatCoordinate(((Blink) message).x)); messageElement.setAttribute(XATTR_Y, ReplayUtils.formatCoordinate(((Blink) message).y)); } } } if (parseActions) { final Boolean sendActionsSelectParam = getBooleanParam(request, PARAM_SEND_ACTIONS_SELECT); final Boolean sendActionsBuildParam = getBooleanParam(request, PARAM_SEND_ACTIONS_BUILD); final Boolean sendActionsTrainParam = getBooleanParam(request, PARAM_SEND_ACTIONS_TRAIN); final Boolean sendActionsResearchParam = getBooleanParam(request, PARAM_SEND_ACTIONS_RESEARCH); final Boolean sendActionsUpgradeParam = getBooleanParam(request, PARAM_SEND_ACTIONS_UPGRADE); final Boolean sendActionsOtherParam = getBooleanParam(request, PARAM_SEND_ACTIONS_OTHER); final Boolean sendActionsInactionParam = getBooleanParam(request, PARAM_SEND_ACTIONS_INACTION); // Default or requested values: final boolean sendActionsSelect = sendActionsSelectParam == null ? false : sendActionsSelectParam; final boolean sendActionsBuild = sendActionsBuildParam == null ? true : sendActionsBuildParam; final boolean sendActionsTrain = sendActionsTrainParam == null ? true : sendActionsTrainParam; final boolean sendActionsResearch = sendActionsResearchParam == null ? true : sendActionsResearchParam; final boolean sendActionsUpgrade = sendActionsUpgradeParam == null ? true : sendActionsUpgradeParam; final boolean sendActionsOther = sendActionsOtherParam == null ? false : sendActionsOtherParam; final boolean sendActionsInaction = sendActionsInactionParam == null ? false : sendActionsInactionParam; final Set<ActionType> sendActionTypeSet = EnumSet.noneOf(ActionType.class); if (sendActionsSelect) sendActionTypeSet.add(ActionType.SELECT); if (sendActionsBuild) sendActionTypeSet.add(ActionType.BUILD); if (sendActionsTrain) sendActionTypeSet.add(ActionType.TRAIN); if (sendActionsResearch) sendActionTypeSet.add(ActionType.RESEARCH); if (sendActionsUpgrade) sendActionTypeSet.add(ActionType.UPGRADE); if (sendActionsOther) sendActionTypeSet.add(ActionType.OTHER); if (sendActionsInaction) sendActionTypeSet.add(ActionType.INACTION); if (sendActionsSelect || sendActionsOther) opsCharged++; if (sendActionsInaction) opsCharged++; xb.setParentElement(null); // Root element final Element actionsElement = xb.createElement( XTAG_ACTIONS, XATTR_ALL_ACTIONS_COUNT, replay.gameEvents.actions.length); actionsElement.setAttribute( XATTR_ERROR_PARSING, Boolean.toString(replay.gameEvents.errorParsing)); xb.setParentElement(actionsElement); int count = 0; // Sent actions count if (!sendActionTypeSet.isEmpty()) { final StringBuilder actionStringBuilder = new StringBuilder(); for (final Action action : replay.gameEvents.actions) if (sendActionTypeSet.contains(action.type)) { count++; final Element actionElement = xb.createElement(XTAG_ACTION_, XATTR_PLAYER_, action.player); actionElement.setAttribute(XATTR_TYPE_, ACTION_TYPE_STRINGS[action.type.ordinal()]); actionElement.setAttribute(XATTR_FRAME_, Integer.toString(action.frame)); actionStringBuilder.setLength(0); action.customToString(actionStringBuilder); actionElement.setAttribute(XATTR_STRING_, actionStringBuilder.toString()); } } actionsElement.setAttribute(XATTR_COUNT, Integer.toString(count)); } xb.printDocument(response); return opsCharged; } catch (final InvalidMpqArchiveException imae) { LOGGER.log(Level.WARNING, "", imae); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid SC2Replay file!"); return 0; } catch (final Exception e) { LOGGER.log(Level.SEVERE, "", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return 0; } }
/** Info operation. */ private long infoOp( final HttpServletRequest request, final HttpServletResponse response, final PersistenceManager pm, final ApiAccount apiAccount) throws IOException { Integer daysCount = getIntParam(request, PARAM_DAYS_COUNT); LOGGER.fine("API account: " + apiAccount.getUser().getEmail() + ", days count: " + daysCount); if (daysCount != null) { if (daysCount < 0 || daysCount > 14) { LOGGER.warning("Invalid days count, must be between 0 and 14!"); response.sendError( HttpServletResponse.SC_BAD_REQUEST, "Invalid days count, must be between 0 and 14!"); return 0; } } else daysCount = 2; try { // Total + days count (last days count) final List<ApiCallStat> apiCallStatList = new JQBuilder<>(pm, ApiCallStat.class) .filter("ownerKey==p1 && day==p2", "KEY p1") .range(0, daysCount + 1) .desc("day") .get(apiAccount.getKey()); final XmlBuilder xb = new XmlBuilder("1.0"); xb.createResultElement(InfoResult.OK); xb.createElement( XTAG_ENGINE_VER, ReplayFactory .getVersion()); // DO NOT USE ReplayFactory.VERSION because the compiler replaces the // actual ReplayFactory.VERSION and in live environment not the value // from sc2gears-parsing-engine.jar will be used! xb.createDateTimeElement(XTAG_SERVER_TIME, new Date()); xb.createElement(XTAG_PAID_OPS, apiAccount.getPaidOps()); xb.createElement( XTAG_AVAIL_OPS, apiCallStatList.isEmpty() ? apiAccount.getPaidOps() : apiAccount.getPaidOps() - apiCallStatList.get(0).getUsedOps()); final Element callStatsElement = xb.createElement(XTAG_CALL_STATS, XATTR_COUNT, apiCallStatList.size()); callStatsElement.setAttribute(XATTR_PATTERN, ApiCallStat.DAY_PATTERN); for (final ApiCallStat apiCallStat : apiCallStatList) { xb.setParentElement(callStatsElement); xb.setParentElement(xb.createElement(XTAG_CALL_STAT, XATTR_DAY, apiCallStat.getDay())); xb.createElement(XTAG_API_CALLS, apiCallStat.getCalls()); xb.createElement(XTAG_USED_OPS, apiCallStat.getUsedOps()); xb.createElement(XTAG_AVG_EXEC_TIME, apiCallStat.getAvgExecTime()) .setAttribute(XATTR_UNIT, "ms"); xb.createElement(XTAG_DENIED_CALLS, apiCallStat.getDeniedCalls()); xb.createElement(XTAG_ERRORS, apiCallStat.getErrors()); xb.createElement(XTAG_INFO_CALLS, apiCallStat.getInfoCalls()); xb.createElement(XTAG_AVG_INFO_EXEC_TIME, apiCallStat.getAvgInfoExecTime()) .setAttribute(XATTR_UNIT, "ms"); xb.createElement(XTAG_MAP_INFO_CALLS, apiCallStat.getMapInfoCalls()); xb.createElement(XTAG_AVG_MAP_INFO_EXEC_TIME, apiCallStat.getAvgMapInfoExecTime()) .setAttribute(XATTR_UNIT, "ms"); xb.createElement(XTAG_PARSE_REP_CALLS, apiCallStat.getParseRepCalls()); xb.createElement(XTAG_AVG_PARSE_REP_EXEC_TIME, apiCallStat.getAvgParseRepExecTime()) .setAttribute(XATTR_UNIT, "ms"); xb.createElement(XTAG_PROF_INFO_CALLS, apiCallStat.getProfInfoCalls()); xb.createElement(XTAG_AVG_PROF_INFO_EXEC_TIME, apiCallStat.getAvgProfInfoExecTime()) .setAttribute(XATTR_UNIT, "ms"); } xb.printDocument(response); return 0; } catch (final Exception e) { LOGGER.log(Level.SEVERE, "", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return 0; } }
/** Get map info operation. */ private long mapInfoOp( final HttpServletRequest request, final HttpServletResponse response, final PersistenceManager pm, final ApiAccount apiAccount) throws IOException { String mapFileName = request.getParameter(PARAM_MAP_FILE_NAME); LOGGER.fine( "API account: " + apiAccount.getUser().getEmail() + ", map file name: " + mapFileName); if (mapFileName == null || mapFileName.isEmpty()) { LOGGER.warning("Missing map file name!"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing map file name!"); return 0; } if ((mapFileName = ServerUtils.checkMapFileName(mapFileName)) == null) { LOGGER.warning("Invalid map file name: " + mapFileName); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid map file name!"); return 0; } try { final XmlBuilder xb = new XmlBuilder("1.0"); long opsCharged = 0; final List<Map> mapList = new JQBuilder<>(pm, Map.class).filter("fname==p1", "String p1").get(mapFileName); if (mapList.isEmpty()) { // Register a task to process the map TaskServlet.register_processMapTask(mapFileName); xb.createResultElement(MapInfoResult.PROCESSING); } else { final Map map = mapList.get(0); switch (map.getStatus()) { case Map.STATUS_PROCESSING: xb.createResultElement(MapInfoResult.PROCESSING); break; case Map.STATUS_PARSING_ERROR: xb.createResultElement(MapInfoResult.PARSING_ERROR); break; case Map.STATUS_DL_ERROR: xb.createResultElement(MapInfoResult.DOWNLOAD_ERROR); break; case Map.STATUS_READY: { opsCharged = 1; xb.createResultElement(MapInfoResult.OK); Element element = xb.createElement(XTAG_MAP); element.setAttribute(XATTR_NAME, map.getName()); element.setAttribute(XATTR_WIDTH, Integer.toString(map.getMwidth())); element.setAttribute(XATTR_HEIGHT, Integer.toString(map.getMheight())); element = xb.createElement(XTAG_MAP_IMAGE); element.setAttribute(XATTR_FORMAT, "JPEG"); element.setAttribute(XATTR_SIZE, Integer.toString(map.getSize())); element.setAttribute(XATTR_WIDTH, Integer.toString(map.getWidth())); element.setAttribute(XATTR_HEIGHT, Integer.toString(map.getHeight())); element.setTextContent( javax.xml.bind.DatatypeConverter.printBase64Binary(map.getImage().getBytes())); break; } } } xb.printDocument(response); return opsCharged; } catch (final Exception e) { LOGGER.log(Level.SEVERE, "", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return 0; } }
@Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final long startNanoTime = System.nanoTime(); final String operation = checkProtVerAndGetOperation(PROTOCOL_VERSION_1, request, response); if (operation == null) return; final String apiKey = request.getParameter(PARAM_API_KEY); if (apiKey == null || apiKey.isEmpty()) { LOGGER.warning("Missing API key!"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing API key!"); return; } long opsCharged = 0; ApiAccount apiAccount = null; PersistenceManager pm = null; ResponseWrapper responseWrapper = null; boolean denied = false; try { pm = PMF.get().getPersistenceManager(); // Check API key final List<ApiAccount> apiAccountList = new JQBuilder<>(pm, ApiAccount.class).filter("apiKey==p1", "String p1").get(apiKey); if (apiAccountList.isEmpty()) { LOGGER.warning("Unauthorized access, invalid API Key: " + apiKey); response.sendError( HttpServletResponse.SC_FORBIDDEN, "Unauthorized access, invalid API Key!"); return; } apiAccount = apiAccountList.get(0); responseWrapper = new ResponseWrapper(response); // Check Ops quota final List<ApiCallStat> totalApiCallStatList = new JQBuilder<>(pm, ApiCallStat.class) .filter("ownerKey==p1 && day==p2", "KEY p1, String p2") .get(apiAccount.getKey(), ApiCallStat.DAY_TOTAL); final long totalUsedOps = totalApiCallStatList.isEmpty() ? 0 : totalApiCallStatList.get(0).getUsedOps(); if (!OPERATION_INFO.equals(operation) && totalUsedOps >= apiAccount.getPaidOps()) { denied = true; LOGGER.warning( "Ops quota have been exceeded, serving denied! (API account: " + apiAccount.getUser().getEmail() + ")"); responseWrapper.sendError( HttpServletResponse.SC_PAYMENT_REQUIRED, "Ops quota have been exceeded, serving denied!"); return; } switch (operation) { case OPERATION_INFO: opsCharged = infoOp(request, responseWrapper, pm, apiAccount); break; case OPERATION_MAP_INFO: opsCharged = mapInfoOp(request, responseWrapper, pm, apiAccount); break; case OPERATION_PARSE_REPLAY: opsCharged = parseRepOp(request, responseWrapper, pm, apiAccount); break; case OPERATION_PROFILE_INFO: opsCharged = profInfoOp(request, responseWrapper, pm, apiAccount); break; default: LOGGER.warning( "Invalid Operation! (API account: " + apiAccount.getUser().getEmail() + ")"); responseWrapper.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid Operation!"); return; } // Notification available Ops will be checked in the task servlet, update API call stat task } finally { if (apiAccount != null) TaskServlet.register_updateApiCallStat( apiAccount.getKey(), apiAccount.getPaidOps(), apiAccount.getNotificationAvailOps(), opsCharged, (System.nanoTime() - startNanoTime) / 1000000l, denied, responseWrapper == null ? true : responseWrapper.isError(), operation); if (pm != null) pm.close(); } }