From aa1ede46d229b2c5ab365d29c519e1b2561238c8 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Sun, 8 Dec 2019 20:41:54 +0000 Subject: [PATCH] Redesign the XHR architecture by splitting the requests. Separate requests are issued for the status table, then a request is triggered when a user clicks on a search. --- .../java/com/muwire/webui/SearchResults.java | 17 ++ .../java/com/muwire/webui/SearchServlet.java | 164 ++++++++++------- webui/src/main/js/search.js | 171 +++++++++--------- 3 files changed, 194 insertions(+), 158 deletions(-) diff --git a/webui/src/main/java/com/muwire/webui/SearchResults.java b/webui/src/main/java/com/muwire/webui/SearchResults.java index 68aa568d..0171a70a 100644 --- a/webui/src/main/java/com/muwire/webui/SearchResults.java +++ b/webui/src/main/java/com/muwire/webui/SearchResults.java @@ -25,13 +25,19 @@ public class SearchResults { private final Map> bySender = new ConcurrentHashMap<>(); private final Map> byInfohash = new ConcurrentHashMap<>(); private final Map> possibleSources = new ConcurrentHashMap<>(); + private volatile long revision; public SearchResults(UUID uuid, String search) { this.uuid = uuid; this.search = search; } + long getRevision() { + return revision; + } + void addResults(UIResultBatchEvent e) { + revision++; Persona sender = e.getResults()[0].getSender(); Set existing = bySender.get(sender); if (existing == null) { @@ -80,5 +86,16 @@ public class SearchResults { public Set getPossibleSources(InfoHash infoHash) { return possibleSources.getOrDefault(infoHash, Collections.emptySet()); } + + int getSenderCount() { + return bySender.size(); + } + + int totalResults() { + int total = 0; + for(Set results : bySender.values()) + total += results.size(); + return total; + } } diff --git a/webui/src/main/java/com/muwire/webui/SearchServlet.java b/webui/src/main/java/com/muwire/webui/SearchServlet.java index f0f79467..a46e8c7b 100644 --- a/webui/src/main/java/com/muwire/webui/SearchServlet.java +++ b/webui/src/main/java/com/muwire/webui/SearchServlet.java @@ -3,6 +3,7 @@ package com.muwire.webui; import java.io.IOException; import java.util.Map; import java.util.Set; +import java.util.UUID; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -46,90 +47,117 @@ public class SearchServlet extends HttpServlet { } StringBuilder sb = new StringBuilder(); sb.append(""); - if (section.equals("groupBySender")) { + if (section.equals("status")) { if (searchManager == null || downloadManager == null) { resp.sendError(403, "Not initialized"); return; } + sb.append(""); for (SearchResults results : searchManager.getResults().values()) { sb.append(""); - sb.append("").append(results.getUUID()).append(""); + sb.append("").append(results.getRevision()).append(""); sb.append("").append(Util.escapeHTMLinXML(results.getSearch())).append(""); - Map> bySender = results.getBySender(); - sb.append(""); - bySender.forEach((sender, resultsFromSender) -> { - sb.append(""); - sb.append(""); - sb.append(Util.escapeHTMLinXML(sender.getHumanReadableName())); - sb.append(""); - sb.append("").append(sender.toBase64()).append(""); - sb.append("").append(resultsFromSender.iterator().next().getBrowse()).append(""); - sb.append("").append(browseManager.isBrowsing(sender)).append(""); - resultsFromSender.forEach(result -> { - sb.append(""); - sb.append(""); - sb.append(Util.escapeHTMLinXML(result.getName())); - sb.append(""); - sb.append(""); - sb.append(DataHelper.formatSize2Decimal(result.getSize(), false)).append("B"); - sb.append(""); - String infohash = Base64.encode(result.getInfohash().getRoot()); - sb.append(""); - sb.append(infohash); - sb.append(""); - sb.append("").append(downloadManager.isDownloading(result.getInfohash())).append(""); - if (result.getComment() != null) { - sb.append("") - .append(Util.escapeHTMLinXML(result.getComment())) - .append(""); - } - sb.append(""); - }); - sb.append(""); - }); - sb.append(""); + sb.append("").append(results.getUUID().toString()).append(""); + sb.append("").append(results.getSenderCount()).append(""); + sb.append("").append(results.totalResults()).append(""); sb.append(""); } - sb.append(""); + sb.append(""); + } else if (section.equals("groupBySender")) { + if (searchManager == null || downloadManager == null) { + resp.sendError(403, "Not initialized"); + return; + } + + String uuidString = req.getParameter("uuid"); + if (uuidString == null) { + resp.sendError(403, "Bad param"); + return; + } + + UUID uuid = UUID.fromString(uuidString); + + SearchResults results = searchManager.getResults().get(uuid); + if (results == null) + return; + + Map> bySender = results.getBySender(); + sb.append(""); + bySender.forEach((sender, resultsFromSender) -> { + sb.append(""); + sb.append(""); + sb.append(Util.escapeHTMLinXML(sender.getHumanReadableName())); + sb.append(""); + sb.append("").append(sender.toBase64()).append(""); + sb.append("").append(resultsFromSender.iterator().next().getBrowse()).append(""); + sb.append("").append(browseManager.isBrowsing(sender)).append(""); + resultsFromSender.forEach(result -> { + sb.append(""); + sb.append(""); + sb.append(Util.escapeHTMLinXML(result.getName())); + sb.append(""); + sb.append(""); + sb.append(DataHelper.formatSize2Decimal(result.getSize(), false)).append("B"); + sb.append(""); + String infohash = Base64.encode(result.getInfohash().getRoot()); + sb.append(""); + sb.append(infohash); + sb.append(""); + sb.append("").append(downloadManager.isDownloading(result.getInfohash())).append(""); + if (result.getComment() != null) { + sb.append("") + .append(Util.escapeHTMLinXML(result.getComment())) + .append(""); + } + sb.append(""); + }); + sb.append(""); + }); + sb.append(""); } else if (section.equals("groupByFile")) { if (searchManager == null || downloadManager == null) { resp.sendError(403, "Not initialized"); return; } - sb.append(""); - for (SearchResults results : searchManager.getResults().values()) { - sb.append(""); - sb.append("").append(results.getUUID()).append(""); - sb.append("").append(Util.escapeHTMLinXML(results.getSearch())).append(""); - Map> byInfohash = results.getByInfoHash(); - sb.append(""); - byInfohash.forEach((infoHash, resultSet) -> { - sb.append(""); - UIResultEvent first = resultSet.iterator().next(); - sb.append("").append(Base64.encode(infoHash.getRoot())).append(""); - sb.append("").append(downloadManager.isDownloading(infoHash)).append(""); - sb.append("").append(Util.escapeHTMLinXML(first.getName())).append(""); - sb.append("").append(DataHelper.formatSize2Decimal(first.getSize(), false)).append("B").append(""); - resultSet.forEach(result -> { - sb.append(""); - sb.append("").append(Util.escapeHTMLinXML(result.getSender().getHumanReadableName())).append(""); - sb.append("").append(result.getSender().toBase64()).append(""); - sb.append("").append(result.getBrowse()).append(""); - sb.append("").append(browseManager.isBrowsing(result.getSender())).append(""); - if (result.getComment() != null) { - sb.append("") - .append(Util.escapeHTMLinXML(result.getComment())) - .append(""); - } - sb.append(""); - }); - sb.append(""); - }); - sb.append(""); - sb.append(""); + + String uuidString = req.getParameter("uuid"); + if (uuidString == null) { + resp.sendError(403, "Bad param"); + return; } - sb.append(""); + + UUID uuid = UUID.fromString(uuidString); + + SearchResults results = searchManager.getResults().get(uuid); + if (results == null) + return; + + Map> byInfohash = results.getByInfoHash(); + sb.append(""); + byInfohash.forEach((infoHash, resultSet) -> { + sb.append(""); + UIResultEvent first = resultSet.iterator().next(); + sb.append("").append(Base64.encode(infoHash.getRoot())).append(""); + sb.append("").append(downloadManager.isDownloading(infoHash)).append(""); + sb.append("").append(Util.escapeHTMLinXML(first.getName())).append(""); + sb.append("").append(DataHelper.formatSize2Decimal(first.getSize(), false)).append("B").append(""); + resultSet.forEach(result -> { + sb.append(""); + sb.append("").append(Util.escapeHTMLinXML(result.getSender().getHumanReadableName())).append(""); + sb.append("").append(result.getSender().toBase64()).append(""); + sb.append("").append(result.getBrowse()).append(""); + sb.append("").append(browseManager.isBrowsing(result.getSender())).append(""); + if (result.getComment() != null) { + sb.append("") + .append(Util.escapeHTMLinXML(result.getComment())) + .append(""); + } + sb.append(""); + }); + sb.append(""); + }); + sb.append(""); } else if (section.equals("connectionsCount")) { if (connectionCounter == null) { resp.sendError(403, "Not initialized"); diff --git a/webui/src/main/js/search.js b/webui/src/main/js/search.js index 2b2831af..e0a68383 100644 --- a/webui/src/main/js/search.js +++ b/webui/src/main/js/search.js @@ -1,7 +1,15 @@ +class SearchStatus { + constructor(xmlNode) { + this.revision = xmlNode.getElementsByTagName("Revision")[0].childNodes[0].nodeValue + this.query = xmlNode.getElementsByTagName("Query")[0].childNodes[0].nodeValue + this.uuid = xmlNode.getElementsByTagName("uuid")[0].childNodes[0].nodeValue + this.senders = xmlNode.getElementsByTagName("Senders")[0].childNodes[0].nodeValue + this.results = xmlNode.getElementsByTagName("Results")[0].childNodes[0].nodeValue + } +} + class SearchBySender { constructor(xmlNode) { - this.uuid = xmlNode.getElementsByTagName("uuid")[0].childNodes[0].nodeValue; - this.query = xmlNode.getElementsByTagName("Query")[0].childNodes[0].nodeValue; this.resultBatches = new Map(); var resultsBySender = xmlNode.getElementsByTagName("ResultsBySender")[0]; @@ -16,8 +24,6 @@ class SearchBySender { class SearchByFile { constructor(xmlNode) { - this.uuid = xmlNode.getElementsByTagName("uuid")[0].childNodes[0].nodeValue; - this.query = xmlNode.getElementsByTagName("Query")[0].childNodes[0].nodeValue; this.resultBatches = new Map(); var resultsByFile = xmlNode.getElementsByTagName("ResultsByFile")[0]; @@ -88,7 +94,9 @@ class ResultByFile { } } -var searches = new Map(); +var statusByUUID = new Map() +var currentSearchBySender = null +var currentSearchByFile = null var expandedComments = new Map(); var uuid = null; @@ -148,7 +156,7 @@ function updateSender(senderName) { var resultsDiv = document.getElementById("bottomTable"); var table = "" - var x = searches.get(uuid) + var x = currentSearchBySender x = x.resultBatches.get(sender).results; for (var [resultInfoHash, result] of x) { table += ""; @@ -191,7 +199,7 @@ function updateSender(senderName) { function updateFile(fileInfoHash) { infoHash = fileInfoHash; - var searchResults = searches.get(uuid).resultBatches.get(infoHash); + var searchResults = currentSearchByFile.resultBatches.get(infoHash); var resultsFromSpan = document.getElementById("resultsFrom"); resultsFromSpan.innerHTML = "Results For "+searchResults.name; @@ -238,11 +246,11 @@ function updateUUIDBySender(resultUUID) { uuid = resultUUID; var currentSearchSpan = document.getElementById("currentSearch"); - currentSearchSpan.innerHTML = searches.get(uuid).query + " Results"; + currentSearchSpan.innerHTML = currentSearchBySender.query + " Results"; var sendersDiv = document.getElementById("topTable"); var table = "
NameSizeDownload
"; - var x = searches.get(uuid).resultBatches; + var x = currentSearchBySender.resultBatches; for (var [senderName, senderBatch] of x) { table += "
SenderBrowse
" table += senderName; @@ -265,12 +273,14 @@ function updateUUIDBySender(resultUUID) { function updateUUIDByFile(resultUUID) { uuid = resultUUID; + var currentStatus = statusByUUID.get(uuid) + var currentSearchSpan = document.getElementById("currentSearch"); - currentSearchSpan.innerHTML = searches.get(uuid).query + " Results"; + currentSearchSpan.innerHTML = currentStatus.query + " Results"; var topTableDiv = document.getElementById("topTable"); var table = ""; - var x = searches.get(uuid).resultBatches; + var x = currentSearchByFile.resultBatches; for (var [fileInfoHash, file] of x) { table += "
NameSizeDownload
"; table += file.name; @@ -292,97 +302,30 @@ function updateUUIDByFile(resultUUID) { updateFile(infoHash); } -function refreshGroupBySender() { +function refreshGroupBySender(searchUUID) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var xmlDoc = this.responseXML; - lastXML = xmlDoc; - searches.clear(); - var i; - var x = xmlDoc.getElementsByTagName("Search"); - for (i = 0; i < x.length; i++) { - var search = new SearchBySender(x[i]); - searches.set(search.uuid, search); - } - - var table = ""; - var activeSearchesDiv = document.getElementById("activeSearches"); - for (var [resultsUUID, search] of searches) { - table += ""; - table += ""; - - var map = new Map(); - for ( var [sender, results] of search.resultBatches ) { - for (var [fileInfoHash, resultFromSender] of results.results) - map.set(resultFromSender.infoHash, 1); - } - table += "" - table += "" - } - table += "
SearchSendersResults
" - table += search.query; - table += "" - table += search.resultBatches.size; - table += ""; - table += map.size; - table += "
" - if (x.length > 0) - activeSearchesDiv.innerHTML = table; - if (uuid != null) - updateUUIDBySender(uuid); - + currentSearchBySender = new SearchBySender(xmlDoc) + updateUUIDBySender(searchUUID); } } - xmlhttp.open("GET", "/MuWire/Search?section=groupBySender", true); + xmlhttp.open("GET", "/MuWire/Search?section=groupBySender&uuid="+searchUUID, true); xmlhttp.send(); } -function refreshGroupByFile() { +function refreshGroupByFile(searchUUID) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var xmlDoc = this.responseXML; - lastXML = xmlDoc; - searches.clear(); - var i; - var x = xmlDoc.getElementsByTagName("Search"); - for (i = 0; i < x.length; i++) { - var search = new SearchByFile(x[i]); - searches.set(search.uuid, search); - } - - var table = ""; - var activeSearchesDiv = document.getElementById("activeSearches"); - for (var [resultsUUID, search] of searches) { - table += ""; - - var map = new Map() - for (var [fileInfoHash, result] of search.resultBatches) { - for (var [senderName, resultFromSender] of result.results) - map.set(senderName, 1) - } - table += ""; - - - table += "" - table += "" - } - table += "
SearchSendersResults
" - table += search.query; - table += "" - table += map.size; - table += ""; - table += search.resultBatches.size; - table += "
" - if (x.length > 0) - activeSearchesDiv.innerHTML = table; - if (uuid != null) - updateUUIDByFile(uuid); + currentSearchByFile = new SearchByFile(xmlDoc) + updateUUIDByFile(searchUUID) } } - xmlhttp.open("GET", "/MuWire/Search?section=groupByFile", true); + xmlhttp.open("GET", "/MuWire/Search?section=groupByFile&uuid="+searchUUID, true); xmlhttp.send(); } @@ -403,12 +346,60 @@ function browse(host) { xmlhttp.send("action=browse&host="+host) } +function refreshStatus() { + var xmlhttp = new XMLHttpRequest() + xmlhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + var currentSearch = null + if (uuid != null) + currentSearch = statusByUUID.get(uuid) + statusByUUID.clear() + + var activeSearches = this.responseXML.getElementsByTagName("Search") + var i + for(i = 0; i < activeSearches.length; i++) { + var searchStatus = new SearchStatus(activeSearches[i]) + statusByUUID.set(searchStatus.uuid, searchStatus) + } + + + var table = "" + for (var [searchUUID, status] of statusByUUID) { + table += "" + table += "" + table += "" + table += "" + table += "" + } + table += "
QuerySendersResults
" + "" + status.query + "" + status.senders + "" + status.results + "
" + + var activeDiv = document.getElementById("activeSearches") + activeDiv.innerHTML = table + + if (uuid != null) { + var newStatus = statusByUUID.get(uuid) + if (newStatus.revision > currentSearch.revision) + refreshFunction(uuid) + } + } + } + xmlhttp.open("GET", "/MuWire/Search?section=status", true) + xmlhttp.send() +} + +var refreshFunction = null +var refreshType = null + function initGroupBySender() { - setInterval(refreshGroupBySender, 3000); - setTimeout(refreshGroupBySender, 1); + refreshFunction = refreshGroupBySender + refreshType = "Sender" + setInterval(refreshStatus, 3000); + setTimeout(refreshStatus, 1); } function initGroupByFile() { - setInterval(refreshGroupByFile, 3000); - setTimeout(refreshGroupByFile, 1); + refreshFunction = refreshGroupByFile + refreshType = "File" + setInterval ( refreshStatus, 3000); + setTimeout ( refreshStatus, 1); }