PIRK-71 Move Querier creation logic into QuerierFactory - closes apache/incubator-pirk#105
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/EncryptionPropertiesBuilder.java b/src/main/java/org/apache/pirk/querier/wideskies/EncryptionPropertiesBuilder.java
new file mode 100644
index 0000000..0cc2579
--- /dev/null
+++ b/src/main/java/org/apache/pirk/querier/wideskies/EncryptionPropertiesBuilder.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pirk.querier.wideskies;
+
+import org.apache.pirk.utils.PIRException;
+
+import java.util.Properties;
+
+import static org.apache.pirk.querier.wideskies.QuerierProps.*;
+
+/**
+ * Holds the various parameters related to creating a {@link Querier}.
+ *
+ * Created by ryan on 9/28/16.
+ */
+public class EncryptionPropertiesBuilder
+{
+    private final Properties properties;
+
+    public static EncryptionPropertiesBuilder newBuilder() {
+      return new EncryptionPropertiesBuilder();
+    }
+
+    private EncryptionPropertiesBuilder() {
+      this.properties = new Properties();
+
+      setGeneralDefaults(properties);
+      setEncryptionDefaults(properties);
+    }
+
+    public EncryptionPropertiesBuilder numThreads(int numThreads) {
+      properties.setProperty(NUMTHREADS, String.valueOf(numThreads));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder bitSet(int bitSet) {
+      properties.setProperty(BITSET, String.valueOf(bitSet));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder queryType(String queryType) {
+      properties.setProperty(QUERYTYPE, queryType);
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder hashBitSize(int hashBitSize) {
+      properties.setProperty(HASHBITSIZE, String.valueOf(hashBitSize));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder hashKey(String hashKey) {
+      properties.setProperty(HASHKEY, hashKey);
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder dataPartitionBitSize(int dataPartitionBitSize) {
+      properties.setProperty(DATAPARTITIONSIZE, String.valueOf(dataPartitionBitSize));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder paillierBitSize(int paillierBitSize) {
+      properties.setProperty(PAILLIERBITSIZE, String.valueOf(paillierBitSize));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder certainty(int certainty) {
+      properties.setProperty(CERTAINTY, String.valueOf(certainty));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder embedSelector(boolean embedSelector) {
+      properties.setProperty(EMBEDSELECTOR, String.valueOf(embedSelector));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder useMemLookupTable(boolean useMemLookupTable) {
+      properties.setProperty(USEMEMLOOKUPTABLE, String.valueOf(useMemLookupTable));
+      return this;
+    }
+
+    public EncryptionPropertiesBuilder useHDFSLookupTable(boolean useHDFSLookupTable) {
+      properties.setProperty(USEHDFSLOOKUPTABLE, String.valueOf(useHDFSLookupTable));
+      return this;
+    }
+
+    public Properties build() throws PIRException
+    {
+      if(!validateQuerierEncryptionProperties(properties)) {
+        throw new PIRException("Encryption properties not valid. See log for details.");
+      }
+      return properties;
+    }
+
+}
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/QuerierCLI.java b/src/main/java/org/apache/pirk/querier/wideskies/QuerierCLI.java
index a8f26cb..8f6e5a1 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/QuerierCLI.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/QuerierCLI.java
@@ -18,18 +18,15 @@
  */
 package org.apache.pirk.querier.wideskies;
 
-import java.io.File;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.GnuParser;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
+import org.apache.commons.cli.*;
+import org.apache.pirk.schema.data.DataSchemaLoader;
+import org.apache.pirk.schema.query.QuerySchemaLoader;
 import org.apache.pirk.utils.SystemConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
+
 /**
  * Class for parsing the command line options for the QuerierDriver
  */
@@ -44,7 +41,6 @@
 
   /**
    * Create and parse allowable options
-   * 
    */
   public QuerierCLI(String[] args)
   {
@@ -80,9 +76,8 @@
 
   /**
    * Determine if an option was provided by the user via the CLI
-   * 
-   * @param option
-   *          - the option of interest
+   *
+   * @param option - the option of interest
    * @return true if option was provided, false otherwise
    */
   public boolean hasOption(String option)
@@ -92,9 +87,8 @@
 
   /**
    * Obtain the argument of the option provided by the user via the CLI
-   * 
-   * @param option
-   *          - the option of interest
+   *
+   * @param option - the option of interest
    * @return value of the argument of the option
    */
   public String getOptionValue(String option)
@@ -104,7 +98,7 @@
 
   /**
    * Method to parse and validate the options provided
-   * 
+   *
    * @return - true if valid, false otherwise
    */
   private boolean parseOptions()
@@ -131,12 +125,28 @@
     // Validate properties
     valid = QuerierProps.validateQuerierProperties();
 
+    // Load the new local query and data schemas
+    if (valid)
+    {
+      logger.info("loading schemas: dataSchemas = " + SystemConfiguration.getProperty("data.schemas") + " querySchemas = " + SystemConfiguration
+          .getProperty("query.schemas"));
+      try
+      {
+        DataSchemaLoader.initialize();
+        QuerySchemaLoader.initialize();
+
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+    }
+
     return valid;
   }
 
   /**
    * Create the options available for the DistributedTestDriver
-   * 
+   *
    * @return Apache's CLI Options object
    */
   private Options createOptions()
@@ -163,22 +173,20 @@
     options.addOption(optionACTION);
 
     // INPUTFILE
-    Option optionINPUTFILE = new Option("i", QuerierProps.INPUTFILE, true,
-        "required - Fully qualified file containing input "
-            + "-- \n The input is either: \n (1) For Encryption: A query file - Contains the query selectors, one per line; "
-            + "the first line must be the query number \n OR \n (2) For Decryption: A response file - Contains the serialized Response object");
+    Option optionINPUTFILE = new Option("i", QuerierProps.INPUTFILE, true, "required - Fully qualified file containing input "
+        + "-- \n The input is either: \n (1) For Encryption: A query file - Contains the query selectors, one per line; "
+        + "the first line must be the query number \n OR \n (2) For Decryption: A response file - Contains the serialized Response object");
     optionINPUTFILE.setRequired(false);
     optionINPUTFILE.setArgName(QuerierProps.INPUTFILE);
     optionINPUTFILE.setType(String.class);
     options.addOption(optionINPUTFILE);
 
     // OUTPUTFILE
-    Option optionOUTPUTFILE = new Option("o", QuerierProps.OUTPUTFILE, true,
-        "required - Fully qualified file for the result output. "
-            + "\n The output file specifies either: \n (1) For encryption: \n \t (a) A file to contain the serialized Querier object named: " + "<outputFile>-"
-            + QuerierConst.QUERIER_FILETAG + "  AND \n \t " + "(b) A file to contain the serialized Query object named: <outputFile>-"
-            + QuerierConst.QUERY_FILETAG + "\n " + "OR \n (2) A file to contain the decryption results where each line is where each line "
-            + "corresponds to one hit and is a JSON object with the schema QuerySchema");
+    Option optionOUTPUTFILE = new Option("o", QuerierProps.OUTPUTFILE, true, "required - Fully qualified file for the result output. "
+        + "\n The output file specifies either: \n (1) For encryption: \n \t (a) A file to contain the serialized Querier object named: " + "<outputFile>-"
+        + QuerierConst.QUERIER_FILETAG + "  AND \n \t " + "(b) A file to contain the serialized Query object named: <outputFile>-" + QuerierConst.QUERY_FILETAG
+        + "\n " + "OR \n (2) A file to contain the decryption results where each line is where each line "
+        + "corresponds to one hit and is a JSON object with the schema QuerySchema");
     optionOUTPUTFILE.setRequired(false);
     optionOUTPUTFILE.setArgName(QuerierProps.OUTPUTFILE);
     optionOUTPUTFILE.setType(String.class);
@@ -244,8 +252,8 @@
 
     // CERTAINTY
     Option optionCERTAINTY = new Option("c", QuerierProps.CERTAINTY, true,
-        "required for encryption -- Certainty of prime generation for Paillier -- must  be greater than or " + "equal to "
-            + SystemConfiguration.getProperty("pir.primeCertainty") + "");
+        "required for encryption -- Certainty of prime generation for Paillier -- must  be greater than or " + "equal to " + SystemConfiguration
+            .getProperty("pir.primeCertainty") + "");
     optionCERTAINTY.setRequired(false);
     optionCERTAINTY.setArgName(QuerierProps.CERTAINTY);
     optionCERTAINTY.setType(String.class);
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java b/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java
index 0338281..2baeec5 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java
@@ -18,18 +18,8 @@
  */
 package org.apache.pirk.querier.wideskies;
 
-import java.io.IOException;
-import java.io.Serializable;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.UUID;
-
-import org.apache.pirk.encryption.Paillier;
 import org.apache.pirk.querier.wideskies.decrypt.DecryptResponse;
-import org.apache.pirk.querier.wideskies.encrypt.EncryptQuery;
-import org.apache.pirk.query.wideskies.QueryInfo;
 import org.apache.pirk.response.wideskies.Response;
-import org.apache.pirk.schema.query.QuerySchemaRegistry;
 import org.apache.pirk.serialization.LocalFileSystemStore;
 import org.apache.pirk.utils.FileIOUtils;
 import org.apache.pirk.utils.PIRException;
@@ -38,6 +28,11 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.UUID;
+
 /**
  * Driver class for encryption of a query or decryption of a response
  * <p>
@@ -84,23 +79,18 @@
     String action;
     String inputFile;
     String outputFile;
-    String queryType = null;
     int numThreads;
     LocalFileSystemStore storage = new LocalFileSystemStore();
 
     // Encryption variables
-    int hashBitSize = 0;
-    String hashKey = null;
-    int dataPartitionBitSize = 0;
-    int paillierBitSize = 0;
-    int certainty = 0;
-    int bitSet = -1;
-    boolean embedSelector = true;
-    boolean useMemLookupTable = false;
-    boolean useHDFSLookupTable = false;
+    int hashBitSize;
+    String hashKey;
+    int dataPartitionBitSize;
+    int paillierBitSize;
+    int certainty;
 
     // Decryption variables
-    String querierFile = null;
+    String querierFile;
 
     // Parse the args
     QuerierCLI qdriverCLI = new QuerierCLI(args);
@@ -112,47 +102,12 @@
     numThreads = Integer.parseInt(SystemConfiguration.getProperty(QuerierProps.NUMTHREADS));
     if (action.equals("encrypt"))
     {
-      queryType = SystemConfiguration.getProperty(QuerierProps.QUERYTYPE);
       hashBitSize = Integer.parseInt(SystemConfiguration.getProperty(QuerierProps.HASHBITSIZE));
       hashKey = SystemConfiguration.getProperty(QuerierProps.HASHKEY);
       dataPartitionBitSize = Integer.parseInt(SystemConfiguration.getProperty(QuerierProps.DATAPARTITIONSIZE));
       paillierBitSize = Integer.parseInt(SystemConfiguration.getProperty(QuerierProps.PAILLIERBITSIZE));
       certainty = Integer.parseInt(SystemConfiguration.getProperty(QuerierProps.CERTAINTY));
-      embedSelector = SystemConfiguration.getBooleanProperty(QuerierProps.EMBEDSELECTOR, true);
-      useMemLookupTable = SystemConfiguration.getBooleanProperty(QuerierProps.USEMEMLOOKUPTABLE, false);
-      useHDFSLookupTable = SystemConfiguration.getBooleanProperty(QuerierProps.USEHDFSLOOKUPTABLE, false);
 
-      if (SystemConfiguration.hasProperty(QuerierProps.BITSET))
-      {
-        bitSet = Integer.parseInt(SystemConfiguration.getProperty(QuerierProps.BITSET));
-        logger.info("bitSet = " + bitSet);
-      }
-
-      // Check to ensure we have a valid queryType
-      if (QuerySchemaRegistry.get(queryType) == null)
-      {
-        logger.error("Invalid schema: " + queryType + "; The following schemas are loaded:");
-        for (String schema : QuerySchemaRegistry.getNames())
-        {
-          logger.info("schema = " + schema);
-        }
-        System.exit(0);
-      }
-
-      // Enforce dataPartitionBitSize < 32
-      if (dataPartitionBitSize > 31)
-      {
-        logger.error("dataPartitionBitSize = " + dataPartitionBitSize + "; must be less than 32");
-      }
-    }
-    if (action.equals("decrypt"))
-    {
-      querierFile = SystemConfiguration.getProperty(QuerierProps.QUERIERFILE);
-    }
-
-    // Perform the action
-    if (action.equals("encrypt"))
-    {
       logger.info("Performing encryption: \n inputFile = " + inputFile + "\n outputFile = " + outputFile + "\n numThreads = " + numThreads + "\n hashBitSize = "
           + hashBitSize + "\n hashKey = " + hashKey + "\n dataPartitionBitSize = " + dataPartitionBitSize + "\n paillierBitSize = " + paillierBitSize
           + "\n certainty = " + certainty);
@@ -165,31 +120,7 @@
       int numSelectors = selectors.size();
       logger.info("queryIdentifier = " + queryIdentifier + " numSelectors = " + numSelectors);
 
-      // Set the necessary QueryInfo and Paillier objects
-      QueryInfo queryInfo = new QueryInfo(queryIdentifier, numSelectors, hashBitSize, hashKey, dataPartitionBitSize, queryType, useMemLookupTable,
-          embedSelector, useHDFSLookupTable);
-
-      if (SystemConfiguration.isSetTrue("pir.embedQuerySchema"))
-      {
-        queryInfo.addQuerySchema(QuerySchemaRegistry.get(queryType));
-      }
-
-      Paillier paillier = new Paillier(paillierBitSize, certainty, bitSet); // throws PIRException if certainty conditions are not satisfied
-
-      // Check the number of selectors to ensure that 2^{numSelector*dataPartitionBitSize} < N
-      // For example, if the highest bit is set, the largest value is \floor{paillierBitSize/dataPartitionBitSize}
-      int exp = numSelectors * dataPartitionBitSize;
-      BigInteger val = (BigInteger.valueOf(2)).pow(exp);
-      if (val.compareTo(paillier.getN()) != -1)
-      {
-        logger.error(
-            "The number of selectors = " + numSelectors + " must be such that " + "2^{numSelector*dataPartitionBitSize} < N = " + paillier.getN().toString(2));
-        System.exit(0);
-      }
-
-      // Perform the encryption
-      EncryptQuery encryptQuery = new EncryptQuery(queryInfo, selectors, paillier);
-      Querier querier = encryptQuery.encrypt(numThreads);
+      Querier querier = QuerierFactory.createQuerier(queryIdentifier, selectors, SystemConfiguration.getProperties());
 
       // Write necessary output files - two files written -
       // (1) Querier object to <outputFile>-QuerierConst.QUERIER_FILETAG
@@ -197,9 +128,10 @@
       storage.store(outputFile + "-" + QuerierConst.QUERIER_FILETAG, querier);
       storage.store(outputFile + "-" + QuerierConst.QUERY_FILETAG, querier.getQuery());
     }
-    else
-    // Decryption
+    else if (action.equals("decrypt"))
     {
+      // Decryption
+      querierFile = SystemConfiguration.getProperty(QuerierProps.QUERIERFILE);
       // Reconstruct the necessary objects from the files
       Response response = storage.recall(inputFile, Response.class);
       Querier querier = storage.recall(querierFile, Querier.class);
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/QuerierFactory.java b/src/main/java/org/apache/pirk/querier/wideskies/QuerierFactory.java
new file mode 100644
index 0000000..f2b8cd7
--- /dev/null
+++ b/src/main/java/org/apache/pirk/querier/wideskies/QuerierFactory.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pirk.querier.wideskies;
+
+import org.apache.pirk.encryption.Paillier;
+import org.apache.pirk.querier.wideskies.encrypt.EncryptQuery;
+import org.apache.pirk.query.wideskies.QueryInfo;
+import org.apache.pirk.schema.query.QuerySchemaRegistry;
+import org.apache.pirk.utils.PIRException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+
+/**
+ * Handles encrypting a query and constructing a {@link Querier} given a {@link EncryptionPropertiesBuilder}.
+ *
+ * Created by ryan on 9/28/16.
+ */
+public class QuerierFactory
+{
+  private static final Logger logger = LoggerFactory.getLogger(QuerierFactory.class);
+
+  /**
+   * Generates a {@link Querier} containing the encrypted query.
+   *
+   * @param queryIdentifier A unique identifier for this query.
+   * @param selectors A list of query selectors.
+   * @param properties A list of properties specifying PIRK configuration options. Use {@link EncryptionPropertiesBuilder} to construct this object.
+   * @return The encrypted query.
+   * @throws PIRException If the provided parameters violate one of the constraints of the PIRK algorithm.
+   * @throws InterruptedException If the encryption process is interrupted.
+   */
+  public static Querier createQuerier(UUID queryIdentifier, List<String> selectors, Properties properties) throws PIRException, InterruptedException
+  {
+    if(!QuerierProps.validateQuerierEncryptionProperties(properties)) {
+      throw new PIRException("Invalid encryption properties.");
+    }
+    int numSelectors = selectors.size();
+    int numThreads = Integer.parseInt(properties.getProperty(QuerierProps.NUMTHREADS));
+    String queryType = properties.getProperty(QuerierProps.QUERYTYPE);
+    int hashBitSize = Integer.parseInt(properties.getProperty(QuerierProps.HASHBITSIZE));
+    int bitSet = Integer.parseInt(properties.getProperty(QuerierProps.BITSET));
+    String hashKey = properties.getProperty(QuerierProps.HASHKEY);
+    int dataPartitionBitSize = Integer.parseInt(properties.getProperty(QuerierProps.DATAPARTITIONSIZE));
+    int paillierBitSize = Integer.parseInt(properties.getProperty(QuerierProps.PAILLIERBITSIZE));
+    int certainty = Integer.parseInt(properties.getProperty(QuerierProps.CERTAINTY));
+    boolean embedSelector = Boolean.valueOf(properties.getProperty(QuerierProps.EMBEDSELECTOR, "false"));
+    boolean useMemLookupTable = Boolean.valueOf(properties.getProperty(QuerierProps.USEMEMLOOKUPTABLE, "false"));
+    boolean useHDFSLookupTable = Boolean.valueOf(properties.getProperty(QuerierProps.USEHDFSLOOKUPTABLE, "false"));
+
+    // Check to ensure we have a valid queryType
+    if (QuerySchemaRegistry.get(queryType) == null)
+    {
+      String message = "Invalid schema: " + queryType + "; The following schemas are loaded: " + QuerySchemaRegistry.getNames();
+      logger.error(message);
+      throw new PIRException(message);
+    }
+
+    // Enforce dataPartitionBitSize < 32
+    if (dataPartitionBitSize > 31)
+    {
+      String message = "dataPartitionBitSize = " + dataPartitionBitSize + "; must be less than 32";
+      logger.error(message);
+      throw new PIRException(message);
+    }
+
+
+    // Set the necessary QueryInfo and Paillier objects
+    QueryInfo queryInfo = new QueryInfo(queryIdentifier, numSelectors, hashBitSize, hashKey, dataPartitionBitSize, queryType, useMemLookupTable,
+        embedSelector, useHDFSLookupTable);
+
+    if ("true".equals(properties.getProperty(QuerierProps.EMBEDQUERYSCHEMA, "false")))
+    {
+      queryInfo.addQuerySchema(QuerySchemaRegistry.get(queryType));
+    }
+
+    Paillier paillier = new Paillier(paillierBitSize, certainty, bitSet); // throws PIRException if certainty conditions are not satisfied
+
+    // Check the number of selectors to ensure that 2^{numSelector*dataPartitionBitSize} < N
+    // For example, if the highest bit is set, the largest value is \floor{paillierBitSize/dataPartitionBitSize}
+    int exp = numSelectors * dataPartitionBitSize;
+    BigInteger val = (BigInteger.valueOf(2)).pow(exp);
+    if (val.compareTo(paillier.getN()) != -1)
+    {
+      String message = "The number of selectors = " + numSelectors + " must be such that " + "2^{numSelector*dataPartitionBitSize} < N = " + paillier.getN().toString(2);
+      logger.error(message);
+      throw new PIRException(message);
+
+    }
+
+    // Perform the encryption
+    EncryptQuery encryptQuery = new EncryptQuery(queryInfo, selectors, paillier);
+    return encryptQuery.encrypt(numThreads);
+  }
+}
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/QuerierProps.java b/src/main/java/org/apache/pirk/querier/wideskies/QuerierProps.java
index 23993cc..5a29a42 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/QuerierProps.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/QuerierProps.java
@@ -18,15 +18,14 @@
  */
 package org.apache.pirk.querier.wideskies;
 
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.pirk.schema.data.DataSchemaLoader;
-import org.apache.pirk.schema.query.QuerySchemaLoader;
 import org.apache.pirk.utils.SystemConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
 /**
  * Properties constants and validation for the Querier
  */
@@ -60,154 +59,179 @@
   // Decryption properties
   static final String QUERIERFILE = "querier.querierFile";
 
-  static final List<String> PROPSLIST = Arrays.asList(ACTION, INPUTFILE, OUTPUTFILE, QUERYTYPE, NUMTHREADS, EMBEDQUERYSCHEMA, HASHBITSIZE, HASHKEY,
-      DATAPARTITIONSIZE, PAILLIERBITSIZE, BITSET, CERTAINTY, QUERYSCHEMAS, DATASCHEMAS, EMBEDSELECTOR, USEMEMLOOKUPTABLE, USEHDFSLOOKUPTABLE, SR_ALGORITHM,
-      SR_PROVIDER);
+  static final List<String> PROPSLIST = Arrays
+      .asList(ACTION, INPUTFILE, OUTPUTFILE, QUERYTYPE, NUMTHREADS, EMBEDQUERYSCHEMA, HASHBITSIZE, HASHKEY, DATAPARTITIONSIZE, PAILLIERBITSIZE, BITSET,
+          CERTAINTY, QUERYSCHEMAS, DATASCHEMAS, EMBEDSELECTOR, USEMEMLOOKUPTABLE, USEHDFSLOOKUPTABLE, SR_ALGORITHM, SR_PROVIDER);
 
-  /**
-   * Validates the querier properties
-   *
-   */
   public static boolean validateQuerierProperties()
   {
+    setGeneralDefaults(SystemConfiguration.getProperties());
+    if(validateGeneralQuerierProperties(SystemConfiguration.getProperties())) {
+      String action = SystemConfiguration.getProperty(ACTION).toLowerCase();
+      // Action is either "encrypt" or "decrypt", or else we can't get here.
+      if(action.equals("encrypt")) {
+        setEncryptionDefaults(SystemConfiguration.getProperties());
+        return validateQuerierEncryptionProperties(SystemConfiguration.getProperties());
+      } else {
+        return validateQuerierDecryptionProperties(SystemConfiguration.getProperties());
+      }
+    } else {
+      return false;
+    }
+  }
+
+  static void setGeneralDefaults(Properties properties) {
+    if (!properties.containsKey(EMBEDQUERYSCHEMA))
+    {
+      properties.setProperty(EMBEDQUERYSCHEMA, "true");
+    }
+    if (!properties.containsKey(NUMTHREADS))
+    {
+      properties.setProperty(NUMTHREADS, String.valueOf(Runtime.getRuntime().availableProcessors()));
+    }
+  }
+
+  public static boolean validateGeneralQuerierProperties(Properties properties)
+  {
     boolean valid = true;
 
     // Parse general required properties
 
-    if (!SystemConfiguration.hasProperty(ACTION))
+    if (!properties.containsKey(ACTION))
     {
       logger.info("Must have the option " + ACTION);
       valid = false;
     }
-    String action = SystemConfiguration.getProperty(ACTION).toLowerCase();
+    String action = properties.getProperty(ACTION).toLowerCase();
     if (!action.equals("encrypt") && !action.equals("decrypt"))
     {
       logger.info("Unsupported action: " + action);
       valid = false;
     }
 
-    if (!SystemConfiguration.hasProperty(INPUTFILE))
+    if (!properties.containsKey(INPUTFILE))
     {
       logger.info("Must have the option " + INPUTFILE);
       valid = false;
     }
 
-    if (!SystemConfiguration.hasProperty(OUTPUTFILE))
+    if (!properties.containsKey(OUTPUTFILE))
     {
       logger.info("Must have the option " + OUTPUTFILE);
       valid = false;
     }
 
-    if (!SystemConfiguration.hasProperty(NUMTHREADS))
+    if (!properties.containsKey(NUMTHREADS))
     {
       logger.info("Must have the option " + NUMTHREADS);
       valid = false;
     }
 
-    // Parse general optional properties
-    if (!SystemConfiguration.hasProperty(EMBEDQUERYSCHEMA))
+    return valid;
+  }
+
+  static void setEncryptionDefaults(Properties properties) {
+    if (!properties.containsKey(EMBEDSELECTOR))
     {
-      SystemConfiguration.setProperty("pir.embedQuerySchema", "true");
+      properties.setProperty(EMBEDSELECTOR, "true");
     }
 
+    if (!properties.containsKey(USEMEMLOOKUPTABLE))
+    {
+      properties.setProperty(USEMEMLOOKUPTABLE, "false");
+    }
+
+    if (!properties.containsKey(USEHDFSLOOKUPTABLE))
+    {
+      properties.setProperty(USEHDFSLOOKUPTABLE, "false");
+    }
+
+    if (!properties.containsKey(BITSET))
+    {
+      properties.setProperty(BITSET, "-1");
+    }
+  }
+
+  public static boolean validateQuerierEncryptionProperties(Properties properties)
+  {
+    boolean valid = true;
+
     // Parse encryption properties
-
-    if (action.equals("encrypt"))
+    if (!properties.containsKey(QUERYTYPE))
     {
-      if (!SystemConfiguration.hasProperty(QUERYTYPE))
-      {
-        logger.info("For action='encrypt': Must have the option " + QUERYTYPE);
-        valid = false;
-      }
-
-      if (!SystemConfiguration.hasProperty(HASHBITSIZE))
-      {
-        logger.info("For action='encrypt': Must have the option " + HASHBITSIZE);
-        valid = false;
-      }
-
-      if (!SystemConfiguration.hasProperty(HASHKEY))
-      {
-        logger.info("For action='encrypt': Must have the option " + HASHKEY);
-        valid = false;
-      }
-
-      if (!SystemConfiguration.hasProperty(DATAPARTITIONSIZE))
-      {
-        logger.info("For action='encrypt': Must have the option " + DATAPARTITIONSIZE);
-        valid = false;
-      }
-
-      if (!SystemConfiguration.hasProperty(PAILLIERBITSIZE))
-      {
-        logger.info("For action='encrypt': Must have the option " + PAILLIERBITSIZE);
-        valid = false;
-      }
-
-      if (!SystemConfiguration.hasProperty(CERTAINTY))
-      {
-        logger.info("For action='encrypt': Must have the option " + CERTAINTY);
-        valid = false;
-      }
-
-      if (!SystemConfiguration.hasProperty(BITSET))
-      {
-        logger.info("For action='encrypt': Must have the option " + BITSET);
-        valid = false;
-      }
-
-      if (SystemConfiguration.hasProperty(QUERYSCHEMAS))
-      {
-        SystemConfiguration.appendProperty("query.schemas", SystemConfiguration.getProperty(QUERYSCHEMAS));
-      }
-
-      if (SystemConfiguration.hasProperty(DATASCHEMAS))
-      {
-        SystemConfiguration.appendProperty("data.schemas", SystemConfiguration.getProperty(DATASCHEMAS));
-      }
-
-      if (!SystemConfiguration.hasProperty(EMBEDSELECTOR))
-      {
-        SystemConfiguration.setProperty(EMBEDSELECTOR, "true");
-      }
-
-      if (!SystemConfiguration.hasProperty(USEMEMLOOKUPTABLE))
-      {
-        SystemConfiguration.setProperty(USEMEMLOOKUPTABLE, "false");
-      }
-
-      if (!SystemConfiguration.hasProperty(USEHDFSLOOKUPTABLE))
-      {
-        SystemConfiguration.setProperty(USEHDFSLOOKUPTABLE, "false");
-      }
+      logger.info("For action='encrypt': Must have the option " + QUERYTYPE);
+      valid = false;
     }
 
-    // Parse decryption args
-    if (action.equals("decrypt"))
+    if (!properties.containsKey(HASHBITSIZE))
     {
-      if (!SystemConfiguration.hasProperty(QUERIERFILE))
-      {
-        logger.info("For action='decrypt': Must have the option " + QUERIERFILE);
-        valid = false;
-      }
+      logger.info("For action='encrypt': Must have the option " + HASHBITSIZE);
+      valid = false;
     }
 
-    // Load the new local query and data schemas
-    if (valid)
+    if (!properties.containsKey(HASHKEY))
     {
-      logger.info("loading schemas: dataSchemas = " + SystemConfiguration.getProperty("data.schemas") + " querySchemas = "
-          + SystemConfiguration.getProperty("query.schemas"));
-      try
-      {
-        DataSchemaLoader.initialize();
-        QuerySchemaLoader.initialize();
+      logger.info("For action='encrypt': Must have the option " + HASHKEY);
+      valid = false;
+    }
 
-      } catch (Exception e)
-      {
-        e.printStackTrace();
-      }
+    if (!properties.containsKey(DATAPARTITIONSIZE))
+    {
+      logger.info("For action='encrypt': Must have the option " + DATAPARTITIONSIZE);
+      valid = false;
+    }
+
+    if (!properties.containsKey(PAILLIERBITSIZE))
+    {
+      logger.info("For action='encrypt': Must have the option " + PAILLIERBITSIZE);
+      valid = false;
+    }
+
+    if (!properties.containsKey(CERTAINTY))
+    {
+      logger.info("For action='encrypt': Must have the option " + CERTAINTY);
+      valid = false;
+    }
+
+    if (properties.containsKey(QUERYSCHEMAS))
+    {
+      appendProperty(properties, "query.schemas", properties.getProperty(QUERYSCHEMAS));
+    }
+
+    if (properties.containsKey(DATASCHEMAS))
+    {
+      appendProperty(properties, "data.schemas", properties.getProperty(DATASCHEMAS));
     }
 
     return valid;
   }
+
+  public static boolean validateQuerierDecryptionProperties(Properties properties)
+  {
+    boolean valid = true;
+
+    // Parse decryption args
+    if (!properties.containsKey(QUERIERFILE))
+    {
+      logger.info("For action='decrypt': Must have the option " + QUERIERFILE);
+      valid = false;
+    }
+
+    return valid;
+  }
+
+  private static void appendProperty(Properties properties, String propertyName, String value)
+  {
+    String oldValue = properties.getProperty(propertyName);
+
+    if (oldValue != null && !oldValue.equals("none"))
+    {
+      oldValue += "," + value;
+    }
+    else
+    {
+      oldValue = value;
+    }
+    properties.setProperty(propertyName, oldValue);
+  }
 }
diff --git a/src/main/java/org/apache/pirk/schema/query/QuerySchemaRegistry.java b/src/main/java/org/apache/pirk/schema/query/QuerySchemaRegistry.java
index 0adae3c..6d7d514 100644
--- a/src/main/java/org/apache/pirk/schema/query/QuerySchemaRegistry.java
+++ b/src/main/java/org/apache/pirk/schema/query/QuerySchemaRegistry.java
@@ -35,11 +35,10 @@
 
   /**
    * Adds the given query schema to the registry.
-   * 
+   * <p>
    * If there was an existing schema with the same name, it is replaced.
-   * 
-   * @param schema
-   *          The query schema to add.
+   *
+   * @param schema The query schema to add.
    * @return the previous schema registered at the same name, or <code>null</code> if there were none.
    */
   public static QuerySchema put(QuerySchema schema)
@@ -49,9 +48,8 @@
 
   /**
    * Returns the query schema with the given name.
-   * 
-   * @param schemaName
-   *          The query schema name to be returned.
+   *
+   * @param schemaName The query schema name to be returned.
    * @return The query schema, or <code>null</code> if no such schema.
    */
   public static QuerySchema get(String schemaName)
@@ -61,7 +59,7 @@
 
   /**
    * Returns the set of query schema names held in the registry.
-   * 
+   *
    * @return The possibly empty set of query schema names.
    */
   public static Set<String> getNames()
diff --git a/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java b/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java
index b86d78c..33533d0 100644
--- a/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java
+++ b/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java
@@ -18,20 +18,12 @@
  */
 package org.apache.pirk.test.utils;
 
-import static org.junit.Assert.fail;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.pirk.encryption.Paillier;
+import org.apache.pirk.querier.wideskies.EncryptionPropertiesBuilder;
 import org.apache.pirk.querier.wideskies.Querier;
 import org.apache.pirk.querier.wideskies.QuerierConst;
+import org.apache.pirk.querier.wideskies.QuerierFactory;
 import org.apache.pirk.querier.wideskies.decrypt.DecryptResponse;
-import org.apache.pirk.querier.wideskies.encrypt.EncryptQuery;
 import org.apache.pirk.query.wideskies.Query;
-import org.apache.pirk.query.wideskies.QueryInfo;
 import org.apache.pirk.query.wideskies.QueryUtils;
 import org.apache.pirk.responder.wideskies.standalone.Responder;
 import org.apache.pirk.response.wideskies.Response;
@@ -41,11 +33,18 @@
 import org.apache.pirk.serialization.LocalFileSystemStore;
 import org.apache.pirk.utils.PIRException;
 import org.apache.pirk.utils.QueryResultsWriter;
-import org.apache.pirk.utils.SystemConfiguration;
 import org.json.simple.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.junit.Assert.fail;
+
 public class StandaloneQuery
 {
   private static final Logger logger = LoggerFactory.getLogger(StandaloneQuery.class);
@@ -77,25 +76,16 @@
     logger.info("fileQuerier = " + fileQuerier.getAbsolutePath() + " fileQuery  = " + fileQuery.getAbsolutePath() + " responseFile = "
         + fileResponse.getAbsolutePath() + " fileFinalResults = " + fileFinalResults.getAbsolutePath());
 
-    boolean embedSelector = SystemConfiguration.getBooleanProperty("pirTest.embedSelector", false);
-    boolean useExpLookupTable = SystemConfiguration.getBooleanProperty("pirTest.useExpLookupTable", false);
-    boolean useHDFSExpLookupTable = SystemConfiguration.getBooleanProperty("pirTest.useHDFSExpLookupTable", false);
+    Properties baseTestEncryptionProperties = EncryptionPropertiesBuilder.newBuilder()
+          .dataPartitionBitSize(BaseTests.dataPartitionBitSize)
+          .hashBitSize(BaseTests.hashBitSize)
+          .hashKey(BaseTests.hashKey)
+          .paillierBitSize(BaseTests.paillierBitSize)
+          .certainty(BaseTests.certainty)
+          .queryType(queryType)
+          .build();
 
-    // Set the necessary objects
-    QueryInfo queryInfo = new QueryInfo(BaseTests.queryIdentifier, selectors.size(), BaseTests.hashBitSize, BaseTests.hashKey, BaseTests.dataPartitionBitSize,
-        queryType, useExpLookupTable, embedSelector, useHDFSExpLookupTable);
-
-    if (SystemConfiguration.getBooleanProperty("pir.embedQuerySchema", false))
-    {
-      queryInfo.addQuerySchema(qSchema);
-    }
-
-    Paillier paillier = new Paillier(BaseTests.paillierBitSize, BaseTests.certainty);
-
-    // Perform the encryption
-    logger.info("Performing encryption of the selectors - forming encrypted query vectors:");
-    EncryptQuery encryptQuery = new EncryptQuery(queryInfo, selectors, paillier);
-    Querier querier = encryptQuery.encrypt(numThreads);
+    Querier querier =  QuerierFactory.createQuerier(BaseTests.queryIdentifier, selectors, baseTestEncryptionProperties);
     logger.info("Completed encryption of the selectors - completed formation of the encrypted query vectors:");
 
     // Dork with the embedSelectorMap to generate a false positive for the last valid selector in selectors
diff --git a/src/main/java/org/apache/pirk/utils/SystemConfiguration.java b/src/main/java/org/apache/pirk/utils/SystemConfiguration.java
index a5c27a9..c4f1c0c 100755
--- a/src/main/java/org/apache/pirk/utils/SystemConfiguration.java
+++ b/src/main/java/org/apache/pirk/utils/SystemConfiguration.java
@@ -79,6 +79,15 @@
   }
 
   /**
+   * Return the Properties object maintained by this class.
+   *
+   * @return The system properties.
+   */
+  public static Properties getProperties() {
+    return props;
+  }
+
+  /**
    * Gets the specified property; returns <code>null</code> if the property isn't found.
    * 
    * @param propertyName