Browse Source

Move requests to asynchronous thread
Correctly cache previous requests
Only fetch the page being viewed
Add method to change number of results per page

Olyol95 8 years ago
parent
commit
5a97951910

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Java template
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+

+ 4 - 1
.idea/libraries/jsoup_1_8_2.xml

@@ -2,8 +2,11 @@
   <library name="jsoup-1.8.2">
     <CLASSES>
       <root url="jar://$USER_HOME$/Downloads/jsoup-1.8.2.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../Server/spigot-1.8.6.jar!/" />
     </CLASSES>
     <JAVADOC />
-    <SOURCES />
+    <SOURCES>
+      <root url="jar://$PROJECT_DIR$/../../Server/spigot-1.8.6.jar!/" />
+    </SOURCES>
   </library>
 </component>

+ 0 - 11
.idea/libraries/spigot_1_8_6.xml

@@ -1,11 +0,0 @@
-<component name="libraryTable">
-  <library name="spigot-1.8.6">
-    <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../spigot/spigot-1.8.6.jar!/" />
-    </CLASSES>
-    <JAVADOC />
-    <SOURCES>
-      <root url="jar://$PROJECT_DIR$/../../spigot/spigot-1.8.6.jar!/" />
-    </SOURCES>
-  </library>
-</component>

+ 0 - 7
.idea/misc.xml

@@ -3,13 +3,6 @@
   <component name="EntryPointsManager">
     <entry_points version="2.0" />
   </component>
-  <component name="MavenImportPreferences">
-    <option name="generalSettings">
-      <MavenGeneralSettings>
-        <option name="mavenHome" value="Bundled (Maven 3)" />
-      </MavenGeneralSettings>
-    </option>
-  </component>
   <component name="ProjectLevelVcsManager" settingsEditedManually="false">
     <OptionsSetting value="true" id="Add" />
     <OptionsSetting value="true" id="Remove" />

+ 0 - 1
Googlr.iml

@@ -7,7 +7,6 @@
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" name="spigot-1.8.6" level="project" />
     <orderEntry type="library" name="jsoup-1.8.2" level="project" />
   </component>
 </module>

+ 38 - 0
src/org/noip/olyol95/googlr/CacheRecord.java

@@ -0,0 +1,38 @@
+package org.noip.olyol95.googlr;
+
+import java.util.ArrayList;
+
+/**
+ * Created by Ollie on 22/08/15.
+ */
+public class CacheRecord {
+
+    private Long timeStamp;
+    private ArrayList<Result> results;
+
+    public CacheRecord(Long timeStamp, ArrayList<Result> results) {
+
+        this.timeStamp = timeStamp;
+        this.results = results;
+
+    }
+
+    public Long getTimeStamp() {
+
+        return timeStamp;
+
+    }
+
+    public boolean isExpired(double expirationPeriod) {
+
+        return (System.currentTimeMillis() - timeStamp) > expirationPeriod;
+
+    }
+
+    public ArrayList<Result> getResults() {
+
+        return results;
+
+    }
+
+}

+ 313 - 60
src/org/noip/olyol95/googlr/Googlr.java

@@ -5,10 +5,9 @@ import org.bukkit.command.Command;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 import org.bukkit.plugin.java.JavaPlugin;
-import org.noip.olyol95.googlr.exceptions.CaptchaRedirectException;
-import org.noip.olyol95.googlr.exceptions.NoQueryFormedException;
+import org.noip.olyol95.googlr.async.QueryProcesserThread;
+import org.noip.olyol95.googlr.sync.QueryResultThread;
 
-import java.io.IOException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Hashtable;
@@ -21,22 +20,28 @@ import java.util.logging.Level;
  */
 public class Googlr extends JavaPlugin {
 
-    public static String BASE_URL = "https://www.google.co.uk/search?q=";
-    public static int RESULTS_PER_PLUGIN_PAGE = 3, RESULTS_PER_GOOGLE_PAGE = 10;
+    //public static String BASE_URL = "https://www.google.co.uk/search?q=";
+    public static int RESULTS_PER_PAGE = 3;
 
     private static long TIMEOUT = 300000L;
 
     private long lastCaptchaTime = 0L;
 
-    private Hashtable<UUID,PlayerContext> playerContexts;
+    private QueryCache queryCache;
+    private QueryProcesserThread queryProcesserThread;
+    private QueryResultThread queryResultThread;
+
+    private Hashtable<UUID,String> playersLastQuery;
+
+    private Hashtable<String,String> locales;
 
     private Class<?> packetPlayOutChat, iChatBaseComponent, packet, craftPlayer, chatSerializer;
     private Method a;
 
-    @Override
-    public void onEnable() {
+    private static Googlr plugin;
 
-        playerContexts = new Hashtable<>();
+    @Override
+    public void onLoad() {
 
         try {
 
@@ -83,17 +88,40 @@ public class Googlr extends JavaPlugin {
 
             getLogger().log(Level.SEVERE,"This plugin is not compatible with your server version/type!");
             setEnabled(false);
-            return;
 
         }
 
     }
 
+    @Override
+    public void onEnable() {
+
+        plugin = this;
+
+        locales = setupLocales();
+
+        playersLastQuery = new Hashtable<>();
+
+        queryCache = new QueryCache();
+        queryResultThread = new QueryResultThread();
+        queryResultThread.runTaskTimer(this,0,10);
+        queryProcesserThread = new QueryProcesserThread();
+        queryProcesserThread.runTaskTimerAsynchronously(this, 10, 20);
+
+    }
+
     @Override
     public void onDisable() {
 
-        playerContexts.clear();
-        playerContexts = null;
+        locales.clear();
+        locales = null;
+
+        queryProcesserThread.cancel();
+
+        queryCache.unload();
+        queryCache = null;
+
+        plugin = null;
 
     }
 
@@ -139,17 +167,9 @@ public class Googlr extends JavaPlugin {
 
                         UUID playerID = ((Player) sender).getUniqueId();
 
-                        PlayerContext playerContext = playerContexts.get(playerID);
-
-                        if (playerContext == null) {
-
-                            playerContext = new PlayerContext();
-                            playerContexts.put(playerID,playerContext);
-
-                        }
-
-                        String query = null;
+                        String searchItem = null;
                         int pageNum = 1;
+                        int resultsPerPage = RESULTS_PER_PAGE;
 
                         for (String arg: args) {
 
@@ -173,70 +193,72 @@ public class Googlr extends JavaPlugin {
 
                                 }
 
-                            } else {
+                            } else if (arg.toLowerCase().startsWith("r:")) {
 
-                                if (query == null) {
+                                try {
 
-                                    query = arg;
+                                    resultsPerPage = Integer.parseInt(arg.split(":")[1]);
 
-                                } else {
+                                    if (resultsPerPage < 1 || resultsPerPage > 10) {
 
-                                    query = query + "+" + arg;
+                                        sender.sendMessage(ChatColor.RED+"Invalid number of results per page: "+arg);
+                                        return true;
 
-                                }
+                                    }
 
-                            }
+                                } catch (NumberFormatException e) {
 
-                        }
+                                    sender.sendMessage(ChatColor.RED+"Invalid number of results per page: "+arg);
+                                    return true;
 
-                        if (query == null) {
+                                }
 
-                            try {
+                            } else {
 
-                                displayResults(sender, playerContext.requestPageOfLastQuery(pageNum), pageNum);
-                                return true;
+                                if (searchItem == null) {
 
-                            } catch (IOException e) {
+                                    searchItem = arg;
 
-                                sender.sendMessage(ChatColor.RED+"There are no more results!");
-                                return true;
+                                } else {
 
-                            } catch (NoQueryFormedException e) {
+                                    searchItem = searchItem + "+" + arg;
 
-                                sender.sendMessage(ChatColor.RED+"You have no searches cached, please specify a query.");
-                                return true;
+                                }
 
-                            } catch (CaptchaRedirectException e) {
+                            }
 
-                                lastCaptchaTime = System.currentTimeMillis();
-                                sender.sendMessage(ChatColor.RED+"The system is unavailable at this time, please try again later.");
-                                return true;
+                        }
 
-                            }
+                        if (searchItem == null) {
 
-                        } else {
+                            if (playersLastQuery.containsKey(playerID)) {
 
-                            try {
+                                searchItem = playersLastQuery.get(playerID);
 
-                                displayResults(sender, playerContext.requestNewQuery(query,pageNum), pageNum);
+                            } else {
+
+                                sender.sendMessage(ChatColor.RED+"You have not made any previous searches, please search again with a valid search item!");
                                 return true;
 
-                            } catch (IOException e) {
+                            }
 
-                                sender.sendMessage(ChatColor.RED+"There are no more results!");
-                                e.printStackTrace();
-                                return true;
+                        }
 
-                            } catch (CaptchaRedirectException e) {
+                        Query query = new Query(playerID,searchItem,pageNum,resultsPerPage);
 
-                                lastCaptchaTime = System.currentTimeMillis();
-                                sender.sendMessage(ChatColor.RED+"The system is unavailable at this time, please try again later.");
-                                return true;
+                        if (queryCache.contains(query.getQueryStringHash())) {
 
-                            }
+                            displayResults(query.getIssuingPlayer(),queryCache.fetch(query.getQueryStringHash()),pageNum);
+
+                        } else {
+
+                            queryProcesserThread.addToQueue(query);
+                            playersLastQuery.put(playerID, query.getSearchItem());
 
                         }
 
+                        return true;
+
                     } else {
 
                         sender.sendMessage(ChatColor.RED+"Insufficient Arguments!");
@@ -259,7 +281,222 @@ public class Googlr extends JavaPlugin {
 
     }
 
-    private void displayResults(CommandSender sender, ArrayList<Result> results, int pageNumber) {
+    private Hashtable<String,String> setupLocales() {
+
+        Hashtable<String,String> localeSetup = new Hashtable<>();
+
+        /**localeSetup.put("af_ZA","co.za/search?");
+         localeSetup.put("am_ET","com.et/search?");
+         localeSetup.put("ar_AE","ae/search?");
+         localeSetup.put("ar_BH","com.bh/search?");
+         localeSetup.put("ar_DZ","dz/search?");
+         localeSetup.put("ar_EG","com.eg/search?");
+         localeSetup.put("ar_IQ","iq/search?");
+         localeSetup.put("ar_JO","jo/search?");
+         localeSetup.put("ar_KW","com.kw/search?");
+         localeSetup.put("ar_LB","com.lb/search?");
+         localeSetup.put("ar_LY","com.ly/search?");
+         localeSetup.put("ar_MA","ma/search?");
+         localeSetup.put("arn_CL","cl/search?");
+         localeSetup.put("ar_OM","com.om/search?");
+         localeSetup.put("ar_QA","com.qa/search?");
+         localeSetup.put("ar_SA","com.sa/search?");
+         localeSetup.put("ar_SY","sy/search?");
+         localeSetup.put("ar_TN","tn/search?");
+         localeSetup.put("ar_YE","ye/search?");
+         localeSetup.put("as_IN","co.in/search?");
+         localeSetup.put("az_AZ","az/search?");
+         localeSetup.put("ba_RU","ru/search?");
+         localeSetup.put("be_BY","by/search?");
+         localeSetup.put("bg_BG","bg/search?");
+         localeSetup.put("bn_BD","com.bd/search?");
+         localeSetup.put("bn_IN","co.in/search?hl=bn&");
+         localeSetup.put("bo_CN","com.hk/search?hl=bo&");
+         localeSetup.put("br_FR","fr/search?hl=br&");
+         localeSetup.put("bs_BA","ba/search?");
+         localeSetup.put("ca_ES","cat/search?");
+         localeSetup.put("cs_CZ","cz/search?");
+         localeSetup.put("cy_GB","com.cy/search?");
+         localeSetup.put("da_DK","dk/search?");
+         localeSetup.put("de_AT","at/search?");
+         localeSetup.put("de_CH","ch/search?");
+         localeSetup.put("de_DE","de/search?");
+         localeSetup.put("de_LI","li/search?");
+         localeSetup.put("de_LU","lu/search?");
+         localeSetup.put("dv_MV","mv/search?");
+         localeSetup.put("el_GR","gr/search?");
+         localeSetup.put("en_029","com.jm/search?");
+         localeSetup.put("en_AU","com.au/search?");
+         localeSetup.put("en_BZ","com.bz/search?");
+         localeSetup.put("en_CA","ca/search?");
+         localeSetup.put("en_GB","co.uk/search?");
+         localeSetup.put("en_IE","ie/search?");
+         localeSetup.put("en_IN","co.in/search?");
+         localeSetup.put("en_JM","com.jm/search?");
+         localeSetup.put("en_MY","com.my/search?");
+         localeSetup.put("en_NZ","co.nz/search?");
+         localeSetup.put("en_PH","com.ph/search?");
+         localeSetup.put("en_SG","com.sg/search?");
+         localeSetup.put("en_TT","tt/search?");
+         localeSetup.put("en_US","com/search?");
+         localeSetup.put("en_ZA","co.za/search?");
+         localeSetup.put("en_ZW","co.zw/search?");
+         localeSetup.put("es_AR","com.ar/search?");
+         localeSetup.put("es_BO","com.bo/search?");
+         localeSetup.put("es_CL","cl/search?hl=es&");
+         localeSetup.put("es_CO","com.co/search?");
+         localeSetup.put("es_CR","co.cr/search?");
+         localeSetup.put("es_DO","com.do/search?");
+         localeSetup.put("es_EC","com.ec/search?");
+         localeSetup.put("es_ES","es/search?");
+         localeSetup.put("es_GT","com.gt/search?");
+         localeSetup.put("es_HN","hn/search?");
+         localeSetup.put("es_MX","mx/search?");
+         localeSetup.put("es_NI","com.ni/search?");
+         localeSetup.put("es_PA","com.pa/search?");
+         localeSetup.put("es_PE","com.pe/search?");
+         localeSetup.put("es_PR","pr/search?");
+         localeSetup.put("es_PY","com.py/search?");
+         localeSetup.put("es_SV","com.sv/search?");
+         localeSetup.put("es_US","com/search?hl=es&");
+         localeSetup.put("es_UY","com.uy/search?");
+         localeSetup.put("es_VE","co.ve/search?");
+         localeSetup.put("et_EE","ee/search?");
+         localeSetup.put("eu_ES","es/search?hl=eu&");
+         localeSetup.put("fa_IR","com/search?hl=fa&");
+         localeSetup.put("fi_FI","fi/search?");
+         localeSetup.put("fil_PH","ph/search?hl=fil&");
+         localeSetup.put("fo_FO","co.uk/search?");
+         localeSetup.put("fr_BE","be/search?");
+         localeSetup.put("fr_CA","ca/search?hl=fr&");
+         localeSetup.put("fr_CH","ch/search?");
+         localeSetup.put("fr_FR","fr/search?");
+         localeSetup.put("fr_LU","lu/search?hl=fr&");
+         localeSetup.put("fr_MC","it/search?hl=fr&");
+         localeSetup.put("fy_NL","nl/search?");
+         localeSetup.put("ga_IE","ie/search?hl=ga&");
+         localeSetup.put("gd_GB","co.ul/search?hl=gd&");
+         localeSetup.put("gl_ES","es/search?hl=gl&");
+         localeSetup.put("gsw_FR","fr/search?hl=gsw&");
+         localeSetup.put("gu_IN", "in.search?gl=gu&");
+         localeSetup.put("ha_NG");
+         localeSetup.put("he_IL");
+         localeSetup.put("hi_IN");
+         localeSetup.put("hr_BA");
+         localeSetup.put("hr_HR");
+         localeSetup.put("hsb_DE");
+         localeSetup.put("hu_HU");
+         localeSetup.put("hy_AM");
+         localeSetup.put("id_ID");
+         localeSetup.put("ig_NG");
+         localeSetup.put("ii_CN");
+         localeSetup.put("is_IS");
+         localeSetup.put("it_CH");
+         localeSetup.put("it_IT");
+         localeSetup.put("iu_CA");
+         localeSetup.put("ja_JP");
+         localeSetup.put("ka_GE");
+         localeSetup.put("kk_KZ");
+         localeSetup.put("kl_GL");
+         localeSetup.put("km_KH");
+         localeSetup.put("kn_IN");
+         localeSetup.put("kok_IN");
+         localeSetup.put("ko_KR");
+         localeSetup.put("ky_KG");
+         localeSetup.put("lb_LU");
+         localeSetup.put("lo_LA");
+         localeSetup.put("lt_LT");
+         localeSetup.put("lv_LV");
+         localeSetup.put("mi_NZ");
+         localeSetup.put("mk_MK");
+         localeSetup.put("ml_IN");
+         localeSetup.put("mn_MN");
+         localeSetup.put("mn_CN");
+         localeSetup.put("moh_CA");
+         localeSetup.put("mr_IN");
+         localeSetup.put("ms_BN");
+         localeSetup.put("ms_MY");
+         localeSetup.put("mt_MT");
+         localeSetup.put("nb_NO");
+         localeSetup.put("ne_NP");
+         localeSetup.put("nl_BE");
+         localeSetup.put("nl_NL");
+         localeSetup.put("nn_NO");
+         localeSetup.put("nso_ZA");
+         localeSetup.put("oc_FR");
+         localeSetup.put("or_IN");
+         localeSetup.put("pa_IN");
+         localeSetup.put("pl_PL");
+         localeSetup.put("prs_AF");
+         localeSetup.put("ps_AF");
+         localeSetup.put("pt_BR");
+         localeSetup.put("pt_PT");
+         localeSetup.put("qut_GT");
+         localeSetup.put("quz_BQ");
+         localeSetup.put("quz_EC");
+         localeSetup.put("quz_PE");
+         localeSetup.put("rm_CH");
+         localeSetup.put("ro_RO");
+         localeSetup.put("ru_RU");
+         localeSetup.put("rw_RW");
+         localeSetup.put("sah_RU");
+         localeSetup.put("sa_IN");
+         localeSetup.put("se_FI");
+         localeSetup.put("se_NO");
+         localeSetup.put("se_SE");
+         localeSetup.put("si_LK");
+         localeSetup.put("sk_SK");
+         localeSetup.put("sl_SI");
+         localeSetup.put("sma_NO");
+         localeSetup.put("sma_SE");
+         localeSetup.put("smj_NO");
+         localeSetup.put("smj_SE");
+         localeSetup.put("smn_FI");
+         localeSetup.put("sms_FI");
+         localeSetup.put("sq_AL");
+         localeSetup.put("sr_BA");
+         localeSetup.put("sr_CS");
+         localeSetup.put("sr_ME");
+         localeSetup.put("sr_RS");
+         localeSetup.put("sv_VI");
+         localeSetup.put("sv_SE");
+         localeSetup.put("sw_KE");
+         localeSetup.put("syr_SY");
+         localeSetup.put("ta_IN");
+         localeSetup.put("te_IN");
+         localeSetup.put("tg_TJ");
+         localeSetup.put("th_TH");
+         localeSetup.put("tk_TM");
+         localeSetup.put("tn_ZA");
+         localeSetup.put("tr_TR");
+         localeSetup.put("tt_RU");
+         localeSetup.put("tzm_DZ");
+         localeSetup.put("ug_CN");
+         localeSetup.put("uk_UA");
+         localeSetup.put("ur_PK");
+         localeSetup.put("uz_UZ");
+         localeSetup.put("vi_VN");
+         localeSetup.put("wo_SN");
+         localeSetup.put("xh_ZA");
+         localeSetup.put("yo_NG");
+         localeSetup.put("zh_CN");
+         localeSetup.put("zh_HK");
+         localeSetup.put("zh_MO");
+         localeSetup.put("zh_SG");
+         localeSetup.put("zh_TW");
+         localeSetup.put("zu_ZA");*/
+
+        return localeSetup;
+
+    }
+
+    public static Googlr instance() {
+
+        return plugin;
+
+    }
+
+    public void displayResults(Player sender, ArrayList<Result> results, int pageNumber) {
 
         sender.sendMessage(ChatColor.GREEN+"----------Search Results-Page:"+pageNumber+"----------");
         sender.sendMessage("");
@@ -294,8 +531,6 @@ public class Googlr extends JavaPlugin {
                 sender.sendMessage(ChatColor.RED+"Error sending link data!");
 
             }
-            //Packet packet = new PacketPlayOutChat(IChatBaseComponent.ChatSerializer.a(json), (byte) 1);
-            //((CraftPlayer) sender).getHandle().playerConnection.sendPacket(packet);
 
             sender.sendMessage(ChatColor.GRAY+result.getBody());
             sender.sendMessage("");
@@ -304,4 +539,22 @@ public class Googlr extends JavaPlugin {
 
     }
 
+    public void setLastCaptchaTime(Long lastCaptchaTime) {
+
+        this.lastCaptchaTime = lastCaptchaTime;
+
+    }
+
+    public QueryCache getQueryCache() {
+
+        return queryCache;
+
+    }
+
+    public QueryResultThread getQueryResultThread() {
+
+        return queryResultThread;
+
+    }
+
 }

+ 0 - 136
src/org/noip/olyol95/googlr/PlayerContext.java

@@ -1,136 +0,0 @@
-package org.noip.olyol95.googlr;
-
-import org.noip.olyol95.googlr.exceptions.CaptchaRedirectException;
-import org.noip.olyol95.googlr.exceptions.NoQueryFormedException;
-import org.noip.olyol95.googlr.util.SearchScraper;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Set;
-
-/**
- * Created by Ollie on 29/05/15.
- */
-public class PlayerContext {
-
-    private Hashtable<Integer,Result> results;
-    private String query;
-
-    public PlayerContext() {
-
-        results = new Hashtable<>();
-        query = "";
-
-    }
-
-    public ArrayList<Result> requestNewQuery(String query, int page) throws IOException, CaptchaRedirectException {
-
-        results.clear();
-
-        return getResultsFromQuery(query,page);
-
-    }
-
-    public ArrayList<Result> requestPageOfLastQuery(int page) throws IOException, NoQueryFormedException, CaptchaRedirectException {
-
-        if (!query.equals("")) {
-
-            return getResultsFromQuery(query, page);
-
-        } else {
-
-            throw new NoQueryFormedException();
-
-        }
-
-    }
-
-    private ArrayList<Result> getResultsFromQuery(String query, int page) throws IOException, CaptchaRedirectException {
-
-        this.query = query;
-
-        ArrayList<Result> resultsToShow = new ArrayList<>();
-
-        int lowerBound = (page - 1) * Googlr.RESULTS_PER_PLUGIN_PAGE;
-        int upperBound = lowerBound + Googlr.RESULTS_PER_PLUGIN_PAGE;
-
-        int multiplier = 0;
-
-        while (!resultsContainBounds(lowerBound, upperBound)) {
-
-            addUniqueResults(executeQuery(query, ((lowerBound / Googlr.RESULTS_PER_GOOGLE_PAGE) + multiplier) * Googlr.RESULTS_PER_GOOGLE_PAGE));
-
-            multiplier++;
-
-            try {
-
-                Thread.sleep(500);
-
-            } catch (InterruptedException e) {
-
-                break;
-
-            }
-
-        }
-
-        for (int x = lowerBound; x < upperBound; x++) {
-
-            resultsToShow.add(results.get(x));
-
-        }
-
-        return resultsToShow;
-
-    }
-
-    private void addUniqueResults(ArrayList<Result> newResults) {
-
-        for (Result result: newResults) {
-
-            if (!results.containsKey(result.getSearchRank())) {
-
-                results.put(result.getSearchRank(),result);
-
-            }
-
-        }
-
-    }
-
-    private boolean resultsContainBounds(int lowerBound, int upperBound) {
-
-        Set<Integer> cachedResults = results.keySet();
-
-        boolean containsBound = true;
-
-        for (int x = lowerBound; x < upperBound; x++) {
-
-            if (!cachedResults.contains(x)) containsBound = false;
-
-        }
-
-        return containsBound;
-
-    }
-
-    private ArrayList<Result> executeQuery(String query, int searchPage) throws IOException, CaptchaRedirectException {
-
-        ArrayList<Result> newResults = SearchScraper.scrapeURL(Googlr.BASE_URL + query + "&start=" + searchPage);
-
-        int index = results.keySet().size();
-
-        for (Result result: newResults) {
-
-            result.setSearchRank(index);
-
-            index++;
-
-        }
-
-        return newResults;
-
-    }
-
-}

+ 81 - 0
src/org/noip/olyol95/googlr/Query.java

@@ -0,0 +1,81 @@
+package org.noip.olyol95.googlr;
+
+import org.bukkit.entity.Player;
+
+import java.util.UUID;
+
+/**
+ * Created by Ollie on 22/08/15.
+ */
+public class Query {
+
+    private static String DEFAULT_BASE_DOMAIN = "https://www.google.co.uk/";
+
+    private UUID playerID;
+    private String searchItem, locale;
+    private int pageNumber, resultsPerPage;
+
+    public Query(UUID playerID, String searchItem, int pageNumber, int resultsPerPage) {
+
+        this.playerID = playerID;
+        this.searchItem = searchItem;
+        this.pageNumber = pageNumber;
+        this.resultsPerPage = resultsPerPage;
+
+    }
+
+    public Query(UUID playerID, String searchItem, int pageNumber, int resultsPerPage, String locale) {
+
+        this.playerID = playerID;
+        this.searchItem = searchItem;
+        this.pageNumber = pageNumber;
+        this.resultsPerPage = resultsPerPage;
+        this.locale = locale;
+
+    }
+
+    public String getQueryString() {
+
+        String queryString = DEFAULT_BASE_DOMAIN+"search?safe=on&num="+resultsPerPage+"&start="+resultsPerPage*(pageNumber-1)+"&q="+searchItem;
+
+        if (locale != null) {
+
+            //todo change base domain
+
+        }
+
+        return queryString;
+
+    }
+
+    public Player getIssuingPlayer() {
+
+        return Googlr.instance().getServer().getPlayer(playerID);
+
+    }
+
+    public UUID getIssuingPlayerID() {
+
+        return playerID;
+
+    }
+
+    public int getPageNumber() {
+
+        return pageNumber;
+
+    }
+
+    public String getSearchItem() {
+
+        return searchItem;
+
+    }
+
+    public int getQueryStringHash() {
+
+        return getQueryString().hashCode();
+
+    }
+
+}

+ 66 - 0
src/org/noip/olyol95/googlr/QueryCache.java

@@ -0,0 +1,66 @@
+package org.noip.olyol95.googlr;
+
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+/**
+ * Created by Ollie on 22/08/15.
+ */
+public class QueryCache {
+
+    private Hashtable<Integer,CacheRecord> cache;
+    private QueryCacheCleaner cacheCleaner;
+
+    public QueryCache() {
+
+        cache = new Hashtable<>();
+        cacheCleaner = new QueryCacheCleaner();
+        cacheCleaner.runTaskTimerAsynchronously(Googlr.instance(),0,6000);
+
+    }
+
+    public void unload() {
+
+        cacheCleaner.cancel();
+        cache.clear();
+
+    }
+
+    public boolean contains(int hash) {
+
+        return cache.containsKey(hash);
+
+    }
+
+    public void store(Query query, ArrayList<Result> results) {
+
+        cache.put(query.getQueryStringHash(), new CacheRecord(System.currentTimeMillis(),results));
+
+    }
+
+    public ArrayList<Result> fetch(int hash) {
+
+        return cache.get(hash).getResults();
+
+    }
+
+    class QueryCacheCleaner extends BukkitRunnable {
+
+        @Override
+        public void run() {
+
+            for (Integer hash: cache.keySet()) {
+
+                if (cache.get(hash).isExpired(300000)) cache.remove(hash);
+
+            }
+
+            System.gc();
+
+        }
+
+    }
+
+}

+ 113 - 0
src/org/noip/olyol95/googlr/async/QueryProcesserThread.java

@@ -0,0 +1,113 @@
+package org.noip.olyol95.googlr.async;
+
+import org.bukkit.ChatColor;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.noip.olyol95.googlr.Googlr;
+import org.noip.olyol95.googlr.Query;
+import org.noip.olyol95.googlr.QueryCache;
+import org.noip.olyol95.googlr.Result;
+import org.noip.olyol95.googlr.exceptions.CaptchaRedirectException;
+import org.noip.olyol95.googlr.util.SearchScraper;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * Created by Ollie on 22/08/15.
+ */
+public class QueryProcesserThread extends BukkitRunnable {
+
+    ArrayList<Query> processQueue;
+    QueryCache queryCache;
+
+    public QueryProcesserThread() {
+
+        processQueue = new ArrayList<>();
+        queryCache = Googlr.instance().getQueryCache();
+
+    }
+
+    @Override
+    public void run() {
+
+        if (!processQueue.isEmpty()) {
+
+            Query query = processQueue.get(0);
+
+            try {
+
+                ArrayList<Result> results = SearchScraper.scrapeURL(query.getQueryString());
+
+                if (!queryCache.contains(query.getQueryStringHash())) {
+
+                    queryCache.store(query, results);
+
+                }
+
+                ArrayList<Object> queryResult = new ArrayList<>();
+                queryResult.add(results);
+                queryResult.add(query);
+                Googlr.instance().getQueryResultThread().insertResult(queryResult);
+
+                processQueue.remove(0);
+
+            } catch (IOException e) {
+
+                ArrayList<Object> queryResult = new ArrayList<>();
+                queryResult.add(ChatColor.RED+"No results found!");
+                queryResult.add(query);
+                Googlr.instance().getQueryResultThread().insertResult(queryResult);
+
+                processQueue.remove(0);
+
+            } catch (CaptchaRedirectException e) {
+
+                Googlr.instance().setLastCaptchaTime(System.currentTimeMillis());
+
+                Iterator<Query> iterator = processQueue.iterator();
+
+                while (iterator.hasNext()) {
+
+                    Query q = iterator.next();
+                    ArrayList<Object> queryResult = new ArrayList<>();
+                    queryResult.add(org.bukkit.ChatColor.RED + "The system is unavailable at this time, please try again later.");
+                    queryResult.add(q);
+                    Googlr.instance().getQueryResultThread().insertResult(queryResult);
+                    iterator.remove();
+
+                }
+
+            }
+
+        }
+
+    }
+
+    public void addToQueue(Query query) {
+
+        processQueue.add(query);
+
+    }
+
+    public void removeFromQueue(Query query) {
+
+        processQueue.remove(query);
+
+    }
+
+    public void removeFromQueue(UUID playerID) {
+
+        Iterator<Query> iterator = processQueue.iterator();
+
+        while (iterator.hasNext()) {
+
+            Query query = iterator.next();
+            if (query.getIssuingPlayerID().equals(playerID)) iterator.remove();
+
+        }
+
+    }
+
+}

+ 61 - 0
src/org/noip/olyol95/googlr/sync/QueryResultThread.java

@@ -0,0 +1,61 @@
+package org.noip.olyol95.googlr.sync;
+
+import org.bukkit.scheduler.BukkitRunnable;
+import org.noip.olyol95.googlr.Googlr;
+import org.noip.olyol95.googlr.Query;
+import org.noip.olyol95.googlr.Result;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Created by Ollie on 22/08/15.
+ */
+public class QueryResultThread extends BukkitRunnable {
+
+    private ArrayList<ArrayList<Object>> outputs;
+
+    public QueryResultThread() {
+
+        outputs = new ArrayList<>();
+
+    }
+
+    @Override
+    public void run() {
+
+        Iterator<ArrayList<Object>> iterator = outputs.iterator();
+
+        while (iterator.hasNext()) {
+
+            ArrayList output = iterator.next();
+
+            if (output.get(0) instanceof ArrayList) {
+
+                ArrayList<Result> results = (ArrayList<Result>) output.get(0);
+                Query query = (Query) output.get(1);
+
+                Googlr.instance().displayResults(query.getIssuingPlayer(), results, query.getPageNumber());
+
+            } else {
+
+                String message = (String) output.get(0);
+                Query query = (Query) output.get(1);
+
+                query.getIssuingPlayer().sendMessage(message);
+
+            }
+
+            iterator.remove();
+
+        }
+
+    }
+
+    public void insertResult(ArrayList<Object> result) {
+
+        outputs.add(result);
+
+    }
+
+}