Revision: 14706
          http://gate.svn.sourceforge.net/gate/?rev=14706&view=rev
Author:   valyt
Date:     2011-12-08 18:14:28 +0000 (Thu, 08 Dec 2011)
Log Message:
-----------
Started work on the new remote query runner.

Modified Paths:
--------------
    mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java

Added Paths:
-----------
    mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunnerMk1.java

Modified: mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java
===================================================================
--- mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java       
2011-12-08 17:35:15 UTC (rev 14705)
+++ mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java       
2011-12-08 18:14:28 UTC (rev 14706)
@@ -15,30 +15,50 @@
 package gate.mimir.search;
 
 import gate.mimir.index.IndexException;
-import gate.mimir.search.QueryRunner;
 import gate.mimir.search.query.Binding;
 import gate.mimir.tool.WebUtils;
 
+import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.objects.ObjectList;
+
 import java.io.IOException;
 import java.io.Serializable;
-import java.net.URLEncoder;
-import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.Executor;
 
 import org.apache.log4j.Logger;
 
+/**
+ * A {@link QueryRunner} implementation that proxies a QueryRunner running on
+ * a remote Mímir server.  
+ */
+public class RemoteQueryRunner implements QueryRunner {
+  
+  protected static final String ACTION_RENDER_DOCUMENT = "renderDocument";
 
-public class RemoteQueryRunner implements QueryRunnerMk1 {
+  protected static final String SERVICE_SEARCH = "search";
+  
+  protected static final String ACTION_DOC_TEXT_BIN = "docTextBin";
+
+  protected static final String ACTION_DOC_URI_BIN = "docURIBin";
+  
+  protected static final String ACTION_DOC_TITLE_BIN = "docTitleBin";
+  
+  protected static final String ACTION_DOC_MEDATADA_FIELDS_BIN = 
"docMetadataFieldsBin";
+  
+  protected static final String ACTION_CLOSE = "close";
+  
+  
   /**
    * The WebUtils instance we use to communicate with the remote
    * index.
    */
   private WebUtils webUtils;
   
+  
   /**
    * The URL to the server hosting the remote index we're searching
    */
@@ -49,45 +69,40 @@
    */
   private String queryId;
 
+  
   /**
-   * The implementation for the background thread action.
+   * Shared Logger
    */
-  private RemoteUpdater backgroundThread;
-
+  private static Logger logger = Logger.getLogger(RemoteQueryRunner.class);
+  
+  
   /**
-   * Holds a local cache of the document statistics from the actual runner. 
Each
-   * element refers to a document, and is an array of 2 ints: the document ID,
-   * and the number of hits respectively. This copy is filled in by a 
background
-   * thread. This value acts as the lock for multi-threaded access to all 
values
-   * that are updated by the {@link RemoteUpdater background thread}.
+   * If the background thread encounters an exception, it
+   * will save it here. As the background thread cannot report it itself, it is
+   * the job of any of the interactive methods to report it.
    */
-  private List<int[]> documentStats;
-
+  private Exception exceptionInBackgroundThread;
+  
+  
   /**
-   * Flag for the state of the query runner. This is a local copy loosely
-   * synchronised with the flag of the actual query runner (on the remote
-   * server). This value is updated by the {@link RemoteUpdater background
-   * thread}.
+   * The document IDs for the documents found to contain hits. This list is
+   * sorted in ascending documentID order.
    */
-  private volatile boolean active;
-
+  protected IntList documentIds;
+  
   /**
-   * Flag for the state of the query runner. This is a local copy loosely
-   * synchronised with the flag of the actual query runner (on the remote
-   * server). This value is updated by the {@link RemoteUpdater background
-   * thread}.
+   * If scoring is enabled ({@link #scorer} is not <code>null</code>), this 
list
+   * contains the scores for the documents found to contain hits. This list is 
+   * aligned to {@link #documentIds}.   
    */
-  private volatile boolean complete;
-
+  protected DoubleArrayList documentScores;
+  
   /**
-   * If the {@link RemoteUpdater background thread} encounters an exception, it
-   * will save it here. As the background thread cannot report it itself, it is
-   * the job of any of the interactive methods to report it.
+   * The sets of hits for each returned document. This data structure is 
lazily 
+   * built, so some elements may be null. 
    */
-  private Exception exceptionInBackgroundThread;
-
-  private Logger logger = Logger.getLogger(RemoteQueryRunner.class);
-
+  protected ObjectList<List<Binding>> documentHits;
+  
   private String getActionBaseUrl(String action) throws IOException{
     //this method is always called from interactive methods, that are capable 
of
     //reporting errors to the user. So we use this place to check if the 
@@ -108,287 +123,127 @@
     return str.toString();
   }
   
-  /**
-   * The action implementation for the background thread responsible for 
reading
-   * the document statistics from the actual remote query runner, and updating
-   * the local cached values.
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentsCount()
    */
-  private class RemoteUpdater implements Runnable {
-    public void run() {
-      try {
-        while(!complete) {
-          if(active) {
-            // update the active flag
-            active = webUtils.getBoolean(
-                    getActionBaseUrl(ACTION_IS_ACTIVE_BIN),
-                    "queryId", queryId);
-            // read the doc count (if we just became inactive (or complete),
-            // these will be the last docs we read in this stage (or ever).
-            int docCount = webUtils.getInt(
-                    getActionBaseUrl(ACTION_DOC_COUNT_BIN),
-                    "queryId", queryId);
-            if(docCount > documentStats.size()) {
-              // get the new data and append to the local cache
-              int[][] newStats = (int[][])webUtils.getObject(
-                      getActionBaseUrl(ACTION_DOC_STATS_BIN),
-                      "queryId", queryId,
-                      "startIndex", Integer.toString(documentStats.size()),
-                      "count", Integer.toString(docCount - 
documentStats.size()));
-              if(newStats != null && newStats.length > 0) {
-                synchronized(documentStats) {
-                  for(int[] stat : newStats) {
-                    documentStats.add(stat);
-                  }
-                }
-              }
-            }
-            // update the complete flag
-            complete = webUtils.getBoolean(
-                    getActionBaseUrl(ACTION_IS_COMPLETE_BIN),
-                    "queryId", queryId);
-            // take a nap
-            Thread.sleep(300);
-          } else {
-            // non-active -> sleep longer
-            Thread.sleep(1000);
-          }
-        }
-      } catch(InterruptedException e) {
-        // pass it on
-        Thread.currentThread().interrupt();
-      } catch(Exception e) {
-        // something went bad
-        logger.error("Cannot communicate with remote service!", e);
-        exceptionInBackgroundThread = e;
-      } finally {
-        // nullify the reference to us from the enclosing object, to mark that
-        // we have finished our work.
-        backgroundThread = null;
-      }
-    }
+  @Override
+  public int getDocumentsCount() {
+    // TODO Auto-generated method stub
+    return 0;
   }
 
-  protected static final String ACTION_CLOSE = "close";
-
-  protected static final String ACTION_DOC_COUNT_BIN = "docCountBin";
-
-  protected static final String ACTION_DOC_STATS_BIN = "docStatsBin";
-
-  protected static final String ACTION_DOC_TEXT_BIN = "docTextBin";
-
-  protected static final String ACTION_DOC_URI_BIN = "docURIBin";
-  
-  protected static final String ACTION_DOC_TITLE_BIN = "docTitleBin";
-  
-  protected static final String ACTION_DOC_MEDATADA_FIELDS_BIN = 
"docMetadataFieldsBin";
-
-  protected static final String ACTION_GET_MORE_HITS = "getMoreHits";
-
-  protected static final String ACTION_HIT_COUNT_BIN = "hitCountBin";
-
-  protected static final String ACTION_HITS_BIN = "hitsBin";
-  
-  protected static final String ACTION_HITS_FOR_DOC_BIN = "hitsForDocumentBin";
-
-  protected static final String ACTION_IS_ACTIVE_BIN = "isActiveBin";
-
-  protected static final String ACTION_IS_COMPLETE_BIN = "isCompleteBin";
-
-  protected static final String ACTION_POST_QUERY_BIN = "postQueryBin";
-
-  protected static final String ACTION_SET_STAGE_MAX_HITS_BIN = 
"setStageMaxHitsBin";
-  
-  protected static final String ACTION_SET_STAGE_TIMEOUT_BIN = 
"setStageTimeoutBin";
-  
-
-  protected static final String ACTION_RENDER_DOCUMENT = "renderDocument";
-
-  protected static final String SERVICE_SEARCH = "search";
-
-  /**
-   * Constructs a new RemoteQueryRunner.
-   * 
-   * @param remoteUrl
-   *          the URL of the remote server holding the actual index being
-   *          queried. This should include the host name, the port (if needed),
-   *          and the web app root name, and the index UUID (e.g.
-   *          http://mimirhost:8080/mimir/1234).
-   * @param queryString the query
-   * @param threadPool
-   * @param webUtils the {@link WebUtils} instance to be used for remote 
-   * communication.
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getCurrentDocumentsCount()
    */
-  public RemoteQueryRunner(String remoteUrl, String queryString,
-          Executor threadPool, WebUtils webUtils) throws IOException {
-    this.webUtils = webUtils;
-    this.remoteUrl = remoteUrl.endsWith("/") ? remoteUrl : (remoteUrl + "/");
-
-    // submit the remote query
-    try {
-      this.queryId = (String) webUtils.getObject(
-              getActionBaseUrl(ACTION_POST_QUERY_BIN), 
-              "queryString", URLEncoder.encode(queryString, "UTF-8"));
-    } catch(ClassNotFoundException e) {
-      //we were expecting a String but got some object of unknown class
-      throw (IOException)new IOException(
-          "Was expecting a String query ID value, but got " +
-               "an unknown object type!").initCause(e);
-    }
-    
-    documentStats = new ArrayList<int[]>();
-    // create the background thread, and start it.
-    this.backgroundThread = new RemoteUpdater();
-    if(threadPool != null) {
-      threadPool.execute(backgroundThread);
-    } else {
-      new Thread(backgroundThread, this.getClass().getCanonicalName()
-              + " background thread").start();
-    }
+  @Override
+  public int getCurrentDocumentsCount() {
+    return documentIds.size();
   }
 
-
-
-
-
-  public void close() throws IOException {
-    webUtils.getVoid(getActionBaseUrl(ACTION_CLOSE),
-            "queryId", queryId);
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentID(int)
+   */
+  @Override
+  public int getDocumentID(int rank) throws IndexOutOfBoundsException,
+          IOException {
+    return documentIds.get(rank);
   }
 
-  public int getDocumentHitsCount(int index) throws IndexOutOfBoundsException {
-    synchronized(documentStats) {
-      return documentStats.get(index)[1];
-    }
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentScore(int)
+   */
+  @Override
+  public double getDocumentScore(int rank) throws IndexOutOfBoundsException,
+          IOException {
+    return documentScores.get(rank);
   }
 
-  public int getDocumentID(int index) throws IndexOutOfBoundsException {
-    synchronized(documentStats) {
-      return documentStats.get(index)[0];
-    }
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentHits(int)
+   */
+  @Override
+  public List<Binding> getDocumentHits(int rank)
+          throws IndexOutOfBoundsException, IOException {
+    return documentHits.get(rank);
   }
 
-  public int getDocumentsCount() {
-    synchronized(documentStats) {
-      return documentStats.size();
-    }
-  }
-
-  @SuppressWarnings("unchecked")
-  public List<Binding> getHits(int startIndex, int hitCount)
-          throws IndexOutOfBoundsException {
-    if(startIndex < 0) {
-      throw new IndexOutOfBoundsException("Negative startIndex: " + 
startIndex);
-    }
-    try {
-      return (List<Binding>)webUtils.getObject(
-             getActionBaseUrl(ACTION_HITS_BIN),
-             "queryId", queryId, 
-             "startIndex", Integer.toString(startIndex),
-             "count", Integer.toString(hitCount));
-    } catch(IOException e) {
-      throw new RuntimeException(e);
-    } catch(ClassNotFoundException e) {
-      throw new RuntimeException("Was expecting a list of bindings, but got " +
-              "an unknown object type!", e);
-    }
-  }
-  
-  
-
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentText(int, int, int)
+   */
   @Override
-  public List<Binding> getHitsForDocument(int documentId)
-    throws IndexOutOfBoundsException {
+  public String[][] getDocumentText(int rank, int termPosition, int length)
+          throws IndexException, IndexOutOfBoundsException, IOException {
     try {
-      return (List<Binding>)webUtils.getObject(
-             getActionBaseUrl(ACTION_HITS_FOR_DOC_BIN),
-             "queryId", queryId, 
-             "documentId", Integer.toString(documentId));
+      return (String[][])webUtils.getObject(
+              getActionBaseUrl(ACTION_DOC_TEXT_BIN),
+              "queryId", queryId,
+              "documentRank", Integer.toString(rank),
+              "termPosition", Integer.toString(termPosition),
+              "length", Integer.toString(length));
     } catch(IOException e) {
-      throw new RuntimeException(e);
+      throw new IndexException(e);
     } catch(ClassNotFoundException e) {
-      throw new RuntimeException("Was expecting a list of bindings, but got " +
-              "an unknown object type!", e);
+      throw new IndexException("Was expecting a bi-dimensional array of" +
+          " Strings, but got an unknown object type!", e);
     }    
   }
 
-  public int getHitsCount() {
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentURI(int)
+   */
+  @Override
+  public String getDocumentURI(int rank) throws IndexException,
+          IndexOutOfBoundsException, IOException {
     try {
-      return webUtils.getInt(getActionBaseUrl(ACTION_HIT_COUNT_BIN),
-             "queryId", queryId);
-    } catch(IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
-  public void getMoreHits() throws IOException {
-    webUtils.getVoid(getActionBaseUrl(ACTION_GET_MORE_HITS),
-            "queryId", queryId);
-    // restart the background thread, if stopped.
-    active = true;
-  }
-
-  public boolean isActive() {
-    return active;
-  }
-
-  public boolean isComplete() {
-    return complete;
-  }
-
-  public void setStageMaxHits(int maxHits) throws IOException{
-    webUtils.getVoid(getActionBaseUrl(ACTION_SET_STAGE_MAX_HITS_BIN),
-            "queryId", queryId, 
-            "maxHits", Integer.toString(maxHits));
-  }
-
-  public void setStageTimeout(int timeout) throws IOException {
-    webUtils.getVoid(getActionBaseUrl(ACTION_SET_STAGE_TIMEOUT_BIN),
-            "queryId", queryId, 
-            "timeout", Integer.toString(timeout));    
-  }
-
-  public String[][] getDocumentText(int documentId, int termPosition, int 
length)
-          throws IndexException {
-    try {
-      return (String[][])webUtils.getObject(
-              getActionBaseUrl(ACTION_DOC_TEXT_BIN),
+      return (String)webUtils.getObject(
+              getActionBaseUrl(ACTION_DOC_URI_BIN),
               "queryId", queryId,
-              "documentId", Integer.toString(documentId),
-              "termPosition", Integer.toString(termPosition),
-              "length", Integer.toString(length));
+              "documentRank", Integer.toString(rank));
     } catch(IOException e) {
       throw new IndexException(e);
     } catch(ClassNotFoundException e) {
-      throw new IndexException("Was expecting a bi-dimensional array of" +
-               " Strings, but got an unknown object type!", e);
+      throw new IndexException("Was expecting a String value, but got an " +
+          "unknown object type!", e);
     }
   }
 
-  public String getDocumentURI(int documentId) throws IndexException {
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentTitle(int)
+   */
+  @Override
+  public String getDocumentTitle(int rank) throws IndexException,
+          IndexOutOfBoundsException, IOException {
     try {
       return (String)webUtils.getObject(
-              getActionBaseUrl(ACTION_DOC_URI_BIN),
+              getActionBaseUrl(ACTION_DOC_TITLE_BIN),
               "queryId", queryId,
-              "documentId", Integer.toString(documentId));
+              "documentRank", Integer.toString(rank));
     } catch(IOException e) {
       throw new IndexException(e);
     } catch(ClassNotFoundException e) {
       throw new IndexException("Was expecting a String value, but got an " +
-               "unknown object type!", e);
+          "unknown object type!", e);
     }
   }
-  
+
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentMetadataField(int, 
java.lang.String)
+   */
   @Override
-  public Serializable getDocumentMetadataField(int documentId, String 
fieldName)
-      throws IndexException {
+  public Serializable getDocumentMetadataField(int rank, String fieldName)
+          throws IndexException, IndexOutOfBoundsException, IOException {
     Set<String> names = new HashSet<String>();
     names.add(fieldName);
-    return getDocumentMetadataFields(documentId, names).get(fieldName);
+    return getDocumentMetadataFields(rank, names).get(fieldName);    
   }
 
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#getDocumentMetadataFields(int, 
java.util.Set)
+   */
   @Override
-  public Map<String, Serializable> getDocumentMetadataFields(int documentId,
-      Set<String> fieldNames) throws IndexException {
+  public Map<String, Serializable> getDocumentMetadataFields(int rank,
+          Set<String> fieldNames) throws IndexException,
+          IndexOutOfBoundsException, IOException {
     try {
       // build a comma-separated value
       StringBuilder namesStr = new StringBuilder();
@@ -404,36 +259,33 @@
       return (Map<String, Serializable>)webUtils.getObject(
               getActionBaseUrl(ACTION_DOC_MEDATADA_FIELDS_BIN),
               "queryId", queryId,
-              "documentId", Integer.toString(documentId),
+              "documentRank", Integer.toString(rank),
               "fieldNames", namesStr.toString());
     } catch(IOException e) {
       throw new IndexException(e);
     } catch(ClassNotFoundException e) {
       throw new IndexException("Was expecting a Map<String, Serializable> " +
-               "value, but got an unknown object type!", e);
+          "value, but got an unknown object type!", e);
     }    
   }
 
-  public String getDocumentTitle(int documentID) throws IndexException {
-    try {
-      return (String)webUtils.getObject(
-              getActionBaseUrl(ACTION_DOC_TITLE_BIN),
-              "queryId", queryId,
-              "documentId", Integer.toString(documentID));
-    } catch(IOException e) {
-      throw new IndexException(e);
-    } catch(ClassNotFoundException e) {
-      throw new IndexException("Was expecting a String value, but got an " +
-          "unknown object type!", e);
-    }
-  }
-
-  
-  public void renderDocument(int documentId, Appendable out)
-          throws IOException, IndexException {
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#renderDocument(int, 
java.lang.Appendable)
+   */
+  @Override
+  public void renderDocument(int rank, Appendable out) throws IOException,
+          IndexException {
     webUtils.getText(out, getActionBaseUrl(ACTION_RENDER_DOCUMENT),
             "queryId", queryId,
-            "documentId", Integer.toString(documentId));
+            "documentRank", Integer.toString(rank));
   }
 
+  /* (non-Javadoc)
+   * @see gate.mimir.search.QueryRunner#close()
+   */
+  @Override
+  public void close() throws IOException {
+    webUtils.getVoid(getActionBaseUrl(ACTION_CLOSE),
+            "queryId", queryId);
+  }
 }

Copied: 
mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunnerMk1.java (from 
rev 14704, 
mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java)
===================================================================
--- mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunnerMk1.java    
                        (rev 0)
+++ mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunnerMk1.java    
2011-12-08 18:14:28 UTC (rev 14706)
@@ -0,0 +1,439 @@
+/*
+ *  RemoteQueryRunnerMk1.java
+ *
+ *  Copyright (c) 2007-2011, The University of Sheffield.
+ *
+ *  This file is part of GATE Mímir (see http://gate.ac.uk/family/mimir.html), 
+ *  and is free software, licenced under the GNU Lesser General Public License,
+ *  Version 3, June 2007 (also included with this distribution as file
+ *  LICENCE-LGPL3.html).
+ *
+ *  Valentin Tablan, 05 Jan 2010
+ *  
+ *  $Id$
+ */
+package gate.mimir.search;
+
+import gate.mimir.index.IndexException;
+import gate.mimir.search.QueryRunner;
+import gate.mimir.search.query.Binding;
+import gate.mimir.tool.WebUtils;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+import org.apache.log4j.Logger;
+
+
+public class RemoteQueryRunnerMk1 implements QueryRunnerMk1 {
+  /**
+   * The WebUtils instance we use to communicate with the remote
+   * index.
+   */
+  private WebUtils webUtils;
+  
+  /**
+   * The URL to the server hosting the remote index we're searching
+   */
+  private String remoteUrl;
+
+  /**
+   * The query ID for the actual query runner, local to the remote index.
+   */
+  private String queryId;
+
+  /**
+   * The implementation for the background thread action.
+   */
+  private RemoteUpdater backgroundThread;
+
+  /**
+   * Holds a local cache of the document statistics from the actual runner. 
Each
+   * element refers to a document, and is an array of 2 ints: the document ID,
+   * and the number of hits respectively. This copy is filled in by a 
background
+   * thread. This value acts as the lock for multi-threaded access to all 
values
+   * that are updated by the {@link RemoteUpdater background thread}.
+   */
+  private List<int[]> documentStats;
+
+  /**
+   * Flag for the state of the query runner. This is a local copy loosely
+   * synchronised with the flag of the actual query runner (on the remote
+   * server). This value is updated by the {@link RemoteUpdater background
+   * thread}.
+   */
+  private volatile boolean active;
+
+  /**
+   * Flag for the state of the query runner. This is a local copy loosely
+   * synchronised with the flag of the actual query runner (on the remote
+   * server). This value is updated by the {@link RemoteUpdater background
+   * thread}.
+   */
+  private volatile boolean complete;
+
+  /**
+   * If the {@link RemoteUpdater background thread} encounters an exception, it
+   * will save it here. As the background thread cannot report it itself, it is
+   * the job of any of the interactive methods to report it.
+   */
+  private Exception exceptionInBackgroundThread;
+
+  private Logger logger = Logger.getLogger(RemoteQueryRunnerMk1.class);
+
+  private String getActionBaseUrl(String action) throws IOException{
+    //this method is always called from interactive methods, that are capable 
of
+    //reporting errors to the user. So we use this place to check if the 
+    //background thread had any problems, and report them if so.
+    if(exceptionInBackgroundThread != null){
+      Exception e = exceptionInBackgroundThread;
+      exceptionInBackgroundThread = null;
+      throw (IOException)new IOException(
+          "Problem communicating with the remote index").initCause(e);
+    }
+    
+    //an example URL looks like this:
+    
//http://localhost:8080/mimir/remote/bf25398f-f087-4224-bfa6-c2ef00399c04/search/hitCountBin?queryId=c4da799e-9ca2-46ae-8ded-30bdc37ad607
+    StringBuilder str = new StringBuilder(remoteUrl);
+    str.append(SERVICE_SEARCH);
+    str.append('/');
+    str.append(action);
+    return str.toString();
+  }
+  
+  /**
+   * The action implementation for the background thread responsible for 
reading
+   * the document statistics from the actual remote query runner, and updating
+   * the local cached values.
+   */
+  private class RemoteUpdater implements Runnable {
+    public void run() {
+      try {
+        while(!complete) {
+          if(active) {
+            // update the active flag
+            active = webUtils.getBoolean(
+                    getActionBaseUrl(ACTION_IS_ACTIVE_BIN),
+                    "queryId", queryId);
+            // read the doc count (if we just became inactive (or complete),
+            // these will be the last docs we read in this stage (or ever).
+            int docCount = webUtils.getInt(
+                    getActionBaseUrl(ACTION_DOC_COUNT_BIN),
+                    "queryId", queryId);
+            if(docCount > documentStats.size()) {
+              // get the new data and append to the local cache
+              int[][] newStats = (int[][])webUtils.getObject(
+                      getActionBaseUrl(ACTION_DOC_STATS_BIN),
+                      "queryId", queryId,
+                      "startIndex", Integer.toString(documentStats.size()),
+                      "count", Integer.toString(docCount - 
documentStats.size()));
+              if(newStats != null && newStats.length > 0) {
+                synchronized(documentStats) {
+                  for(int[] stat : newStats) {
+                    documentStats.add(stat);
+                  }
+                }
+              }
+            }
+            // update the complete flag
+            complete = webUtils.getBoolean(
+                    getActionBaseUrl(ACTION_IS_COMPLETE_BIN),
+                    "queryId", queryId);
+            // take a nap
+            Thread.sleep(300);
+          } else {
+            // non-active -> sleep longer
+            Thread.sleep(1000);
+          }
+        }
+      } catch(InterruptedException e) {
+        // pass it on
+        Thread.currentThread().interrupt();
+      } catch(Exception e) {
+        // something went bad
+        logger.error("Cannot communicate with remote service!", e);
+        exceptionInBackgroundThread = e;
+      } finally {
+        // nullify the reference to us from the enclosing object, to mark that
+        // we have finished our work.
+        backgroundThread = null;
+      }
+    }
+  }
+
+  protected static final String ACTION_CLOSE = "close";
+
+  protected static final String ACTION_DOC_COUNT_BIN = "docCountBin";
+
+  protected static final String ACTION_DOC_STATS_BIN = "docStatsBin";
+
+  protected static final String ACTION_DOC_TEXT_BIN = "docTextBin";
+
+  protected static final String ACTION_DOC_URI_BIN = "docURIBin";
+  
+  protected static final String ACTION_DOC_TITLE_BIN = "docTitleBin";
+  
+  protected static final String ACTION_DOC_MEDATADA_FIELDS_BIN = 
"docMetadataFieldsBin";
+
+  protected static final String ACTION_GET_MORE_HITS = "getMoreHits";
+
+  protected static final String ACTION_HIT_COUNT_BIN = "hitCountBin";
+
+  protected static final String ACTION_HITS_BIN = "hitsBin";
+  
+  protected static final String ACTION_HITS_FOR_DOC_BIN = "hitsForDocumentBin";
+
+  protected static final String ACTION_IS_ACTIVE_BIN = "isActiveBin";
+
+  protected static final String ACTION_IS_COMPLETE_BIN = "isCompleteBin";
+
+  protected static final String ACTION_POST_QUERY_BIN = "postQueryBin";
+
+  protected static final String ACTION_SET_STAGE_MAX_HITS_BIN = 
"setStageMaxHitsBin";
+  
+  protected static final String ACTION_SET_STAGE_TIMEOUT_BIN = 
"setStageTimeoutBin";
+  
+
+  protected static final String ACTION_RENDER_DOCUMENT = "renderDocument";
+
+  protected static final String SERVICE_SEARCH = "search";
+
+  /**
+   * Constructs a new RemoteQueryRunnerMk1.
+   * 
+   * @param remoteUrl
+   *          the URL of the remote server holding the actual index being
+   *          queried. This should include the host name, the port (if needed),
+   *          and the web app root name, and the index UUID (e.g.
+   *          http://mimirhost:8080/mimir/1234).
+   * @param queryString the query
+   * @param threadPool
+   * @param webUtils the {@link WebUtils} instance to be used for remote 
+   * communication.
+   */
+  public RemoteQueryRunnerMk1(String remoteUrl, String queryString,
+          Executor threadPool, WebUtils webUtils) throws IOException {
+    this.webUtils = webUtils;
+    this.remoteUrl = remoteUrl.endsWith("/") ? remoteUrl : (remoteUrl + "/");
+
+    // submit the remote query
+    try {
+      this.queryId = (String) webUtils.getObject(
+              getActionBaseUrl(ACTION_POST_QUERY_BIN), 
+              "queryString", URLEncoder.encode(queryString, "UTF-8"));
+    } catch(ClassNotFoundException e) {
+      //we were expecting a String but got some object of unknown class
+      throw (IOException)new IOException(
+          "Was expecting a String query ID value, but got " +
+               "an unknown object type!").initCause(e);
+    }
+    
+    documentStats = new ArrayList<int[]>();
+    // create the background thread, and start it.
+    this.backgroundThread = new RemoteUpdater();
+    if(threadPool != null) {
+      threadPool.execute(backgroundThread);
+    } else {
+      new Thread(backgroundThread, this.getClass().getCanonicalName()
+              + " background thread").start();
+    }
+  }
+
+
+
+
+
+  public void close() throws IOException {
+    webUtils.getVoid(getActionBaseUrl(ACTION_CLOSE),
+            "queryId", queryId);
+  }
+
+  public int getDocumentHitsCount(int index) throws IndexOutOfBoundsException {
+    synchronized(documentStats) {
+      return documentStats.get(index)[1];
+    }
+  }
+
+  public int getDocumentID(int index) throws IndexOutOfBoundsException {
+    synchronized(documentStats) {
+      return documentStats.get(index)[0];
+    }
+  }
+
+  public int getDocumentsCount() {
+    synchronized(documentStats) {
+      return documentStats.size();
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public List<Binding> getHits(int startIndex, int hitCount)
+          throws IndexOutOfBoundsException {
+    if(startIndex < 0) {
+      throw new IndexOutOfBoundsException("Negative startIndex: " + 
startIndex);
+    }
+    try {
+      return (List<Binding>)webUtils.getObject(
+             getActionBaseUrl(ACTION_HITS_BIN),
+             "queryId", queryId, 
+             "startIndex", Integer.toString(startIndex),
+             "count", Integer.toString(hitCount));
+    } catch(IOException e) {
+      throw new RuntimeException(e);
+    } catch(ClassNotFoundException e) {
+      throw new RuntimeException("Was expecting a list of bindings, but got " +
+              "an unknown object type!", e);
+    }
+  }
+  
+  
+
+  @Override
+  public List<Binding> getHitsForDocument(int documentId)
+    throws IndexOutOfBoundsException {
+    try {
+      return (List<Binding>)webUtils.getObject(
+             getActionBaseUrl(ACTION_HITS_FOR_DOC_BIN),
+             "queryId", queryId, 
+             "documentId", Integer.toString(documentId));
+    } catch(IOException e) {
+      throw new RuntimeException(e);
+    } catch(ClassNotFoundException e) {
+      throw new RuntimeException("Was expecting a list of bindings, but got " +
+              "an unknown object type!", e);
+    }    
+  }
+
+  public int getHitsCount() {
+    try {
+      return webUtils.getInt(getActionBaseUrl(ACTION_HIT_COUNT_BIN),
+             "queryId", queryId);
+    } catch(IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public void getMoreHits() throws IOException {
+    webUtils.getVoid(getActionBaseUrl(ACTION_GET_MORE_HITS),
+            "queryId", queryId);
+    // restart the background thread, if stopped.
+    active = true;
+  }
+
+  public boolean isActive() {
+    return active;
+  }
+
+  public boolean isComplete() {
+    return complete;
+  }
+
+  public void setStageMaxHits(int maxHits) throws IOException{
+    webUtils.getVoid(getActionBaseUrl(ACTION_SET_STAGE_MAX_HITS_BIN),
+            "queryId", queryId, 
+            "maxHits", Integer.toString(maxHits));
+  }
+
+  public void setStageTimeout(int timeout) throws IOException {
+    webUtils.getVoid(getActionBaseUrl(ACTION_SET_STAGE_TIMEOUT_BIN),
+            "queryId", queryId, 
+            "timeout", Integer.toString(timeout));    
+  }
+
+  public String[][] getDocumentText(int documentId, int termPosition, int 
length)
+          throws IndexException {
+    try {
+      return (String[][])webUtils.getObject(
+              getActionBaseUrl(ACTION_DOC_TEXT_BIN),
+              "queryId", queryId,
+              "documentId", Integer.toString(documentId),
+              "termPosition", Integer.toString(termPosition),
+              "length", Integer.toString(length));
+    } catch(IOException e) {
+      throw new IndexException(e);
+    } catch(ClassNotFoundException e) {
+      throw new IndexException("Was expecting a bi-dimensional array of" +
+               " Strings, but got an unknown object type!", e);
+    }
+  }
+
+  public String getDocumentURI(int documentId) throws IndexException {
+    try {
+      return (String)webUtils.getObject(
+              getActionBaseUrl(ACTION_DOC_URI_BIN),
+              "queryId", queryId,
+              "documentId", Integer.toString(documentId));
+    } catch(IOException e) {
+      throw new IndexException(e);
+    } catch(ClassNotFoundException e) {
+      throw new IndexException("Was expecting a String value, but got an " +
+               "unknown object type!", e);
+    }
+  }
+  
+  @Override
+  public Serializable getDocumentMetadataField(int documentId, String 
fieldName)
+      throws IndexException {
+    Set<String> names = new HashSet<String>();
+    names.add(fieldName);
+    return getDocumentMetadataFields(documentId, names).get(fieldName);
+  }
+
+  @Override
+  public Map<String, Serializable> getDocumentMetadataFields(int documentId,
+      Set<String> fieldNames) throws IndexException {
+    try {
+      // build a comma-separated value
+      StringBuilder namesStr = new StringBuilder();
+      boolean first = true;
+      for(String aName : fieldNames) {
+        if(first) {
+          first = false;
+        } else {
+          namesStr.append(", ");
+        }
+        namesStr.append(aName.replace(",", "\\,"));
+      }
+      return (Map<String, Serializable>)webUtils.getObject(
+              getActionBaseUrl(ACTION_DOC_MEDATADA_FIELDS_BIN),
+              "queryId", queryId,
+              "documentId", Integer.toString(documentId),
+              "fieldNames", namesStr.toString());
+    } catch(IOException e) {
+      throw new IndexException(e);
+    } catch(ClassNotFoundException e) {
+      throw new IndexException("Was expecting a Map<String, Serializable> " +
+               "value, but got an unknown object type!", e);
+    }    
+  }
+
+  public String getDocumentTitle(int documentID) throws IndexException {
+    try {
+      return (String)webUtils.getObject(
+              getActionBaseUrl(ACTION_DOC_TITLE_BIN),
+              "queryId", queryId,
+              "documentId", Integer.toString(documentID));
+    } catch(IOException e) {
+      throw new IndexException(e);
+    } catch(ClassNotFoundException e) {
+      throw new IndexException("Was expecting a String value, but got an " +
+          "unknown object type!", e);
+    }
+  }
+
+  
+  public void renderDocument(int documentId, Appendable out)
+          throws IOException, IndexException {
+    webUtils.getText(out, getActionBaseUrl(ACTION_RENDER_DOCUMENT),
+            "queryId", queryId,
+            "documentId", Integer.toString(documentId));
+  }
+
+}

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Cloud Services Checklist: Pricing and Packaging Optimization
This white paper is intended to serve as a reference, checklist and point of 
discussion for anyone considering optimizing the pricing and packaging model 
of a cloud services business. Read Now!
http://www.accelacomm.com/jaw/sfnl/114/51491232/
_______________________________________________
GATE-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/gate-cvs

Reply via email to