edu.umass.cs.gnsserver.gnsapp.clientCommandProcessor.commandSupport.FieldAccess.java Source code

Java tutorial

Introduction

Here is the source code for edu.umass.cs.gnsserver.gnsapp.clientCommandProcessor.commandSupport.FieldAccess.java

Source

/*
 *
 *  Copyright (c) 2015 University of Massachusetts
 *
 *  Licensed 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.
 *
 *  Initial developer(s): Westy
 *
 */
package edu.umass.cs.gnsserver.gnsapp.clientCommandProcessor.commandSupport;

import edu.umass.cs.gnscommon.ResponseCode;
import edu.umass.cs.gnscommon.SharedGuidUtils;
import edu.umass.cs.gnscommon.GNSProtocol;
import edu.umass.cs.gnscommon.exceptions.server.FailedDBOperationException;
import edu.umass.cs.gnscommon.exceptions.server.FieldNotFoundException;
import edu.umass.cs.gnscommon.exceptions.server.InternalRequestException;
import edu.umass.cs.gnscommon.exceptions.server.RecordNotFoundException;
import edu.umass.cs.gnscommon.packets.CommandPacket;
import edu.umass.cs.gnsserver.database.ColumnFieldType;
import edu.umass.cs.gnsserver.main.GNSConfig;
import edu.umass.cs.gnsserver.utils.ResultValue;
import edu.umass.cs.gnscommon.utils.Base64;
import edu.umass.cs.gnsserver.gnsapp.Select;
import edu.umass.cs.gnsserver.gnsapp.clientCommandProcessor.ClientRequestHandlerInterface;
import edu.umass.cs.gnsserver.gnsapp.clientSupport.AclCheckResult;
import edu.umass.cs.gnsserver.gnsapp.clientSupport.NSAuthentication;
import edu.umass.cs.gnsserver.gnsapp.clientSupport.NSFieldAccess;
import edu.umass.cs.gnsserver.gnsapp.clientSupport.NSUpdateSupport;
import edu.umass.cs.gnsserver.gnsapp.packet.SelectOperation;
import edu.umass.cs.gnsserver.utils.ValuesMap;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Arrays;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import edu.umass.cs.gnsserver.gnsapp.GNSApplicationInterface;
import edu.umass.cs.gnsserver.gnsapp.clientSupport.NSAccessSupport;
import edu.umass.cs.gnsserver.gnsapp.packet.SelectGroupBehavior;
import edu.umass.cs.gnsserver.gnsapp.packet.SelectRequestPacket;
import edu.umass.cs.gnsserver.gnsapp.packet.SelectResponsePacket;
import edu.umass.cs.gnsserver.interfaces.InternalRequestHeader;
import edu.umass.cs.utils.Config;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.time.DateUtils;

/**
 * Provides static methods for sending and retrieve data values to and from the
 * the database which stores the fields and values.
 * Provides conversion between the database to java objects.
 *
 *
 * @author westy, arun
 */
public class FieldAccess {

    private final static Logger LOGGER = Logger.getLogger(FieldAccess.class.getName());

    private static final String EMPTY_JSON_ARRAY_STRING = new JSONArray().toString();
    private static final String EMPTY_STRING = "";

    /* false means that even single field queries will return a JSONObject response
     * with a single key and value. The client code has been modified accordingly.
     * The server-side modifications involve changes to AccountAccess to handle
     * lookupGuidLocally and lookupPrimaryGuid differently.
     */
    /**
     *
     */
    protected static final boolean SINGLE_FIELD_VALUE_ONLY = false;//true;

    /**
     * Reads the value of field in a guid.
     * Field(s) is a string the naming the field(s). Field(s) can us dot
     * notation to indicate subfields.
     *
     * @param header
     * @param commandPacket
     *
     * @param guid
     * @param field - mutually exclusive with fields
     * @param reader
     * @param signature
     * @param message
     * @param timestamp
     * @param handler
     * @return the value of a single field
     */
    public static CommandResponse lookupSingleField(InternalRequestHeader header, CommandPacket commandPacket,
            String guid, String field, String reader, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {
        ResponseCode errorCode = signatureAndACLCheckForRead(header, commandPacket, guid, field, null, // fields
                reader, signature, message, timestamp, handler.getApp());
        if (errorCode.isExceptionOrError()) {
            return new CommandResponse(errorCode,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + errorCode.getProtocolCode());
        }
        ValuesMap valuesMap;
        try {
            valuesMap = NSFieldAccess.lookupJSONFieldLocally(header, guid, field, handler.getApp());
            // note: reader can also be null here
            if (!header.verifyInternal()) {
                // don't strip internal fields when doing a read for other servers
                valuesMap = valuesMap.removeInternalFields();
            }
            if (valuesMap != null) {
                /* arun: changed to not rely on JSONException. The previous code was relying
                  on valuesMap.getString() throwing a JSONException and conveying a GNSProtocol.JSON_PARSE_ERROR.toString(), 
                  which is incorrect in this case because it should give a FIELD_NOT_FOUND_EXCEPTION
                  to the client.
                 */
                if (valuesMap.isNull(field)) {
                    return new CommandResponse(ResponseCode.FIELD_NOT_FOUND_EXCEPTION,
                            GNSProtocol.BAD_RESPONSE.toString() + " " + GNSProtocol.FIELD_NOT_FOUND.toString() + " "
                                    + guid + ":" + field + " ");
                } else {
                    // arun: added support for SINGLE_FIELD_VALUE_ONLY flag
                    return new CommandResponse(ResponseCode.NO_ERROR,
                            SINGLE_FIELD_VALUE_ONLY ? valuesMap.getString(field) : valuesMap.toString());
                }
            } else {
                return new CommandResponse(ResponseCode.NO_ERROR, EMPTY_STRING);
            }

        } catch (FailedDBOperationException e) {
            return new CommandResponse(ResponseCode.DATABASE_OPERATION_ERROR, GNSProtocol.BAD_RESPONSE.toString()
                    + " " + GNSProtocol.DATABASE_OPERATION_ERROR.toString() + " " + e);
        } catch (JSONException e) {
            return new CommandResponse(ResponseCode.JSON_PARSE_ERROR,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + GNSProtocol.JSON_PARSE_ERROR.toString() + " " + e);
        }

    }

    /**
     * Reads the value of fields in a guid.
     * Field(s) is a string the naming the field(s). Field(s) can us dot
     * notation to indicate subfields.
     *
     * @param header
     * @param commandPacket
     *
     * @param guid
     * @param fields - mutually exclusive with field
     * @param reader
     * @param signature
     * @param message
     * @param timestamp
     * @param handler
     * @return the value of a single field
     */
    public static CommandResponse lookupMultipleFields(InternalRequestHeader header, CommandPacket commandPacket,
            String guid, ArrayList<String> fields, String reader, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {
        ResponseCode errorCode = signatureAndACLCheckForRead(header, commandPacket, guid, null, //field
                fields, reader, signature, message, timestamp, handler.getApp());
        if (errorCode.isExceptionOrError()) {
            return new CommandResponse(errorCode,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + errorCode.getProtocolCode());
        }
        ValuesMap valuesMap;
        try {
            valuesMap = NSFieldAccess.lookupFieldsLocalNoAuth(header, guid, fields, ColumnFieldType.USER_JSON,
                    handler);
            // note: reader can also be null here
            if (!header.verifyInternal()) {
                // don't strip internal fields when doing a read for other servers
                valuesMap = valuesMap.removeInternalFields();
            }
            return new CommandResponse(ResponseCode.NO_ERROR, valuesMap.toString()); // multiple field return
        } catch (FailedDBOperationException e) {
            return new CommandResponse(ResponseCode.DATABASE_OPERATION_ERROR, GNSProtocol.BAD_RESPONSE.toString()
                    + " " + GNSProtocol.DATABASE_OPERATION_ERROR.toString() + " " + e);
        }

    }

    /**
     * Supports reading of the old style data formatted as a JSONArray of strings.
     * Much of the internal system data is still stored in this format.
     * The new format also supports JSONArrays as part of the
     * whole "guid data is a JSONObject" format so we might be
     * transitioning away from this altogether at some point.
     *
     * @param header
     * @param commandPacket
     * @param guid
     * @param field
     * @param reader
     * @param signature
     * @param timestamp
     * @param message
     * @param handler
     * @return a command response
     */
    public static CommandResponse lookupJSONArray(InternalRequestHeader header, CommandPacket commandPacket,
            String guid, String field, String reader, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {

        ResponseCode errorCode = signatureAndACLCheckForRead(header, commandPacket, guid, field, null, // fields
                reader, signature, message, timestamp, handler.getApp());
        if (errorCode.isExceptionOrError()) {
            return new CommandResponse(errorCode,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + errorCode.getProtocolCode());
        }
        String resultString;
        ResultValue value = NSFieldAccess.lookupListFieldLocallySafe(guid, field, handler.getApp().getDB());
        if (!value.isEmpty()) {
            try {
                resultString = new JSONObject().put(field, value).toString();
            } catch (JSONException e) {
                return new CommandResponse(ResponseCode.JSON_PARSE_ERROR,
                        GNSProtocol.BAD_RESPONSE.toString() + " " + ResponseCode.JSON_PARSE_ERROR);
            }
        } else {
            resultString = new JSONObject().toString();
        }
        return new CommandResponse(ResponseCode.NO_ERROR, resultString);
    }

    /**
     * Reads the value of all the fields in a guid.
     * Doesn't return internal system fields.
     *
     * @param header
     * @param commandPacket
     * @param guid
     * @param reader
     * @param signature
     * @param message
     * @param handler
     * @param timestamp
     * @return a command response
     */
    public static CommandResponse lookupMultipleValues(InternalRequestHeader header, CommandPacket commandPacket,
            String guid, String reader, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {

        ResponseCode errorCode = FieldAccess.signatureAndACLCheckForRead(header, commandPacket, guid,
                GNSProtocol.ENTIRE_RECORD.toString(), null, //fields
                reader, signature, message, timestamp, handler.getApp());
        if (errorCode.isExceptionOrError()) {
            return new CommandResponse(errorCode,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + errorCode.getProtocolCode());
        }
        String resultString;
        ResponseCode responseCode;
        try {
            ValuesMap valuesMap = NSFieldAccess.lookupJSONFieldLocally(header, guid,
                    GNSProtocol.ENTIRE_RECORD.toString(), handler.getApp());
            if (valuesMap != null) {
                resultString = valuesMap.removeInternalFields().toString();
                responseCode = ResponseCode.NO_ERROR;
            } else {
                resultString = GNSProtocol.BAD_RESPONSE.toString();
                responseCode = ResponseCode.BAD_GUID_ERROR;
            }
        } catch (FailedDBOperationException e) {
            resultString = GNSProtocol.BAD_RESPONSE.toString();
            responseCode = ResponseCode.DATABASE_OPERATION_ERROR;
        }
        return new CommandResponse(responseCode, resultString);
    }

    /**
     * Returns the first value of the field in a guid in a an old-style JSONArray field value.
     *
     * @param header
     * @param commandPacket
     * @param guid
     * @param field
     * @param reader
     * @param signature
     * @param message
     * @param timestamp
     * @param handler
     * @return a command response
     */
    public static CommandResponse lookupOne(InternalRequestHeader header, CommandPacket commandPacket, String guid,
            String field, String reader, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {

        ResponseCode errorCode = signatureAndACLCheckForRead(header, commandPacket, guid, field, null, //fields
                reader, signature, message, timestamp, handler.getApp());
        if (errorCode.isExceptionOrError()) {
            return new CommandResponse(errorCode,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + errorCode.getProtocolCode());
        }
        String resultString;
        ResultValue value = NSFieldAccess.lookupListFieldLocallySafe(guid, field, handler.getApp().getDB());
        if (!value.isEmpty()) {
            Object singleValue = value.get(0);
            if (singleValue instanceof Number) {
                resultString = singleValue.toString();
            } else {
                resultString = (String) value.get(0);
            }
        } else {
            return new CommandResponse(ResponseCode.FIELD_NOT_FOUND_ERROR,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + GNSProtocol.FIELD_NOT_FOUND.toString());
        }
        return new CommandResponse(ResponseCode.NO_ERROR, resultString);
    }

    /**
     * Returns the first value of all the fields in a guid in an old-style JSONArray field value.
     *
     * @param header
     * @param commandPacket
     * @param guid
     * @param reader
     * @param signature
     * @param message
     * @param timestamp
     * @param handler
     * @return a command response
     */
    public static CommandResponse lookupOneMultipleValues(InternalRequestHeader header, CommandPacket commandPacket,
            String guid, String reader, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {

        ResponseCode errorCode = FieldAccess.signatureAndACLCheckForRead(header, commandPacket, guid,
                GNSProtocol.ENTIRE_RECORD.toString(), null, //fields
                reader, signature, message, timestamp, handler.getApp());
        if (errorCode.isExceptionOrError()) {
            return new CommandResponse(errorCode,
                    GNSProtocol.BAD_RESPONSE.toString() + " " + errorCode.getProtocolCode());
        }
        String resultString;
        ResponseCode responseCode;
        try {
            ValuesMap valuesMap = NSFieldAccess.lookupJSONFieldLocally(null, guid,
                    GNSProtocol.ENTIRE_RECORD.toString(), handler.getApp());
            if (valuesMap != null) {
                resultString = valuesMap.removeInternalFields().toJSONObjectFirstOnes().toString();
                responseCode = ResponseCode.NO_ERROR;
            } else {
                resultString = GNSProtocol.BAD_RESPONSE.toString();
                responseCode = ResponseCode.BAD_GUID_ERROR;
            }
        } catch (FailedDBOperationException e) {
            resultString = GNSProtocol.BAD_RESPONSE.toString();
            responseCode = ResponseCode.DATABASE_OPERATION_ERROR;
        } catch (JSONException e) {
            resultString = GNSProtocol.BAD_RESPONSE.toString() + " " + GNSProtocol.JSON_PARSE_ERROR.toString() + " "
                    + e.getMessage();
            responseCode = ResponseCode.JSON_PARSE_ERROR;
        }
        return new CommandResponse(responseCode, resultString);
    }

    /**
     * Updates the field with value.
     *
     * @param header
     * @param commandPacket
     * @param guid - the guid to update
     * @param key - the field to update
     * @param value - the new value
     * @param oldValue - the old value - only applicable for certain operations, null otherwise
     * @param argument - for operations that require an index, -1 otherwise
     * @param operation - the update operation to perform... see <code>UpdateOperation</code>
     * @param writer - the guid performing the write operation, can be the same as the guid being written. Can be null for globally
     * readable or writable fields or the secret for internal operations done without a signature.
     * @param signature - the signature of the request. Used for authentication at the server. Can be null for globally
     * readable or writable fields or the secret for internal operations done without a signature.
     * @param message - the message that was signed. Used for authentication at the server. Can be null for globally
     * readable or writable fields or the secret for internal operations done without a signature.
     * @param timestamp
     * @param handler
     * @return an NSResponseCode
     */
    public static ResponseCode update(InternalRequestHeader header, CommandPacket commandPacket, String guid,
            String key, String value, String oldValue, int argument, UpdateOperation operation, String writer,
            String signature, String message, Date timestamp, ClientRequestHandlerInterface handler) {
        return update(header, commandPacket, guid, key, new ResultValue(Arrays.asList(value)),
                oldValue != null ? new ResultValue(Arrays.asList(oldValue)) : null, argument, operation, writer,
                signature, message, timestamp, handler);
    }

    /**
     * Updates the field with value.
     *
     * @param header
     * @param commandPacket
     * @param guid - the guid to update
     * @param key - the field to update
     * @param value - the new value
     * @param oldValue - the old value - only applicable for certain operations, null otherwise
     * @param argument - for operations that require an index, -1 otherwise
     * @param operation - the update operation to perform... see <code>UpdateOperation</code>
     * @param writer - the guid performing the write operation, can be the same as the guid being written. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param signature - the signature of the request. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param message - the message that was signed. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param timestamp
     * @param handler
     * @return an NSResponseCode
     */
    public static ResponseCode update(InternalRequestHeader header, CommandPacket commandPacket, String guid,
            String key, ResultValue value, ResultValue oldValue, int argument, UpdateOperation operation,
            String writer, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {

        try {
            return NSUpdateSupport.executeUpdateLocal(header, commandPacket, guid, key, writer, signature, message,
                    timestamp, operation, value, oldValue, argument, null, handler.getApp(), false);
        } catch (JSONException e) {
            LOGGER.log(Level.FINE, "Update threw error: {0}", e);
            return ResponseCode.JSON_PARSE_ERROR;
        } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | SignatureException
                | IOException | InternalRequestException | FailedDBOperationException | RecordNotFoundException
                | FieldNotFoundException e) {
            LOGGER.log(Level.FINE, "Update threw error: {0}", e);
            return ResponseCode.UPDATE_ERROR;
        }
    }

    /**
     * Sends an update request to the server containing a JSON Object.
     *
     * @param header
     * @param guid - the guid to update
     * @param json - the JSONObject to use in the update
     * @param operation - the update operation to perform... see <code>UpdateOperation</code>
     * @param writer - the guid performing the write operation, can be the same as the guid being written. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param signature - the signature of the request. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param message - the message that was signed. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param handler
     * @return an NSResponseCode
     */
    private static ResponseCode update(InternalRequestHeader header, CommandPacket commandPacket, String guid,
            JSONObject json, UpdateOperation operation, String writer, String signature, String message,
            Date timestamp, ClientRequestHandlerInterface handler) {
        try {
            return NSUpdateSupport.executeUpdateLocal(header, commandPacket, guid, null, writer, signature, message,
                    timestamp, operation, null, null, -1, new ValuesMap(json), handler.getApp(), false);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | SignatureException
                | JSONException | IOException | InternalRequestException | FailedDBOperationException
                | RecordNotFoundException | FieldNotFoundException e) {
            LOGGER.log(Level.FINE, "Update threw error: {0}", e);
            return ResponseCode.UPDATE_ERROR;
        }
    }

    /**
     * Sends an update request to the server containing a JSON Object.
     *
     * @param header
     * @param commandPacket
     * @param guid - the guid to update
     * @param json - the JSONObject to use in the update
     * @param writer - the guid performing the write operation, can be the same as the guid being written. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param signature - the signature of the request. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param message - the message that was signed. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param timestamp
     * @param handler
     * @return an NSResponseCode
     */
    public static ResponseCode updateUserJSON(InternalRequestHeader header, CommandPacket commandPacket,
            String guid, JSONObject json, String writer, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {
        return FieldAccess.update(header, commandPacket, guid, new ValuesMap(json),
                UpdateOperation.USER_JSON_REPLACE, writer, signature, message, timestamp, handler);
    }

    /**
     * Sends an update request to the server containing a JSON Object.
     * This is a convenience method - one could use the <code>update</code> method.
     *
     * @param header
     * @param commandPacket
     * @param guid - the guid to update
     * @param key - the field to createField
     * @param value - the initial value of the field
     * @param writer - the guid performing the createField operation, can be the same as the guid being written. Can be null for globally
     * readable or writable fields or the secret for internal operations done without a signature.
     * @param signature - the signature of the request. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param message - the message that was signed. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param timestamp
     * @param handler
     * @return a {@link ResponseCode}
     */
    public static ResponseCode createField(InternalRequestHeader header, CommandPacket commandPacket, String guid,
            String key, ResultValue value, String writer, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {
        return update(header, commandPacket, guid, key, value, null, -1, UpdateOperation.SINGLE_FIELD_CREATE,
                writer, signature, message, timestamp, handler);
    }

    /**
     * Deletes the field from the guid.
     *
     * @param header
     * @param commandPacket
     * @param guid - the guid to update
     * @param key - the field to createField
     * @param writer - the guid performing the delete operation, can be the same as the guid being written.
     * Can be null for globally readable or writable fields or the secret for internal operations done without a signature.
     * @param signature - the signature of the request. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param message - the message that was signed. Used for authentication at the server. Can be null for globally
     * readable or writable fields or for internal operations done without a signature.
     * @param timestamp
     * @param handler
     * @return a {@link ResponseCode}
     */
    public static ResponseCode deleteField(InternalRequestHeader header, CommandPacket commandPacket, String guid,
            String key, String writer, String signature, String message, Date timestamp,
            ClientRequestHandlerInterface handler) {
        return update(header, commandPacket, guid, key, "", null, -1, // these are ignored anyway
                UpdateOperation.SINGLE_FIELD_REMOVE_FIELD, writer, signature, message, timestamp, handler);
    }

    ///
    /// SELECT METHODS
    ///
    private static JSONArray executeSelect(InternalRequestHeader header, CommandPacket commandPacket,
            SelectOperation operation, String reader, String key, Object value, Object otherValue, String signature,
            String message, GNSApplicationInterface<String> app)
            throws FailedDBOperationException, JSONException, UnknownHostException, InternalRequestException {
        SelectRequestPacket packet = new SelectRequestPacket(-1, operation, SelectGroupBehavior.NONE, reader, key,
                value, otherValue);
        return executeSelectHelper(header, commandPacket, packet, reader, signature, message, app);
    }

    private static JSONArray executeSelectHelper(InternalRequestHeader header, CommandPacket commandPacket,
            SelectRequestPacket packet, String reader, String signature, String message,
            GNSApplicationInterface<String> app)
            throws FailedDBOperationException, JSONException, UnknownHostException, InternalRequestException {
        // First do a signature check 
        if (!signatureCheckForSelect(reader, signature, message, app)) {
            return null;
        }

        SelectResponsePacket responsePacket = Select.handleSelectRequestFromClient(header, packet, app);
        if (responsePacket != null && // Fixme: probably should just have handleSelectRequestFromClient throw a clientException
                SelectResponsePacket.ResponseCode.NOERROR.equals(responsePacket.getResponseCode())) {
            if (packet.getProjection() == null) {
                return responsePacket.getGuids();
            } else {
                return responsePacket.getRecords();
            }
        } else {
            return null;
        }
    }

    private static boolean signatureCheckForSelect(String reader, String signature, String message,
            GNSApplicationInterface<String> app) {
        try {
            if (signature == null || reader == null) {
                // Return true... later check will catch fields that aren't world readable
                LOGGER.log(Level.FINE, "Signature check for select: reader={0} signature={1}",
                        new Object[] { reader, signature });
                return true;
            }
            boolean result = NSAccessSupport.verifySignature(
                    NSAuthentication.lookupPublicKeyLocallyWithCacheing(reader, app), signature, message);
            LOGGER.log(Level.FINE, "Signature check for select: reader={0} result={1}",
                    new Object[] { reader, result });
            return result;
        } catch (FailedDBOperationException | InvalidKeyException | SignatureException
                | UnsupportedEncodingException | InvalidKeySpecException e) {
            LOGGER.log(Level.FINE, "Signature check for select: reader={0} error={1}",
                    new Object[] { reader, e.getMessage() });
            return false;
        }
    }

    /**
     * Sends a select request to the server to retrieve all the guids matching the request.
     *
     * @param header
     * @param commandPacket
     * @param reader
     * @param key - the key to match
     * @param value - the value to match
     * @param signature
     * @param message
     * @param handler
     * @return a command response
     * @throws InternalRequestException
     */
    public static CommandResponse select(InternalRequestHeader header, CommandPacket commandPacket, String reader,
            String key, Object value, String signature, String message, ClientRequestHandlerInterface handler)
            throws InternalRequestException {
        JSONArray result;
        try {
            result = executeSelect(header, commandPacket, SelectOperation.EQUALS, reader, key, value, null,
                    signature, message, handler.getApp());
            if (result != null) {
                return new CommandResponse(ResponseCode.NO_ERROR, result.toString());
            }
        } catch (IOException | JSONException | FailedDBOperationException e) {
            // FIXME: why silently fail?
        }
        return new CommandResponse(ResponseCode.NO_ERROR, EMPTY_JSON_ARRAY_STRING);
    }

    /**
     * Sends a select request to the server to retrieve all the guids within an area specified by a bounding box.
     *
     * @param header
     * @param commandPacket
     * @param reader
     * @param key - the field to match - should be a location field
     * @param value - a bounding box
     * @param signature
     * @param message
     * @param handler
     * @return a command response
     * @throws InternalRequestException
     */
    public static CommandResponse selectWithin(InternalRequestHeader header, CommandPacket commandPacket,
            String reader, String key, String value, String signature, String message,
            ClientRequestHandlerInterface handler) throws InternalRequestException {
        JSONArray result;
        try {
            result = executeSelect(header, commandPacket, SelectOperation.WITHIN, reader, key, value, null,
                    signature, message, handler.getApp());
            if (result != null) {
                return new CommandResponse(ResponseCode.NO_ERROR, result.toString());
            }
        } catch (IOException | JSONException | FailedDBOperationException e) {
            // FIXME: why silently fail?
        }
        return new CommandResponse(ResponseCode.NO_ERROR, EMPTY_JSON_ARRAY_STRING);

    }

    /**
     * Sends a select request to the server to retrieve all the guids within maxDistance of value.
     *
     * @param header
     * @param commandPacket
     * @param reader
     * @param key - the field to match - should be a location field
     * @param value - the position
     * @param maxDistance - the maximum distance from position
     * @param signature
     * @param message
     * @param handler
     * @return a command response
     * @throws InternalRequestException
     */
    public static CommandResponse selectNear(InternalRequestHeader header, CommandPacket commandPacket,
            String reader, String key, String value, String maxDistance, String signature, String message,
            ClientRequestHandlerInterface handler) throws InternalRequestException {
        JSONArray result;
        try {
            result = executeSelect(header, commandPacket, SelectOperation.NEAR, reader, key, value, maxDistance,
                    signature, message, handler.getApp());
            if (result != null) {
                return new CommandResponse(ResponseCode.NO_ERROR, result.toString());
            }
        } catch (IOException | JSONException | FailedDBOperationException e) {
            // FIXME: why silently fail?
        }
        return new CommandResponse(ResponseCode.NO_ERROR, EMPTY_JSON_ARRAY_STRING);
    }

    /**
     * Sends a select request to the server to retrieve all the guid matching the query.
     *
     * @param header
     * @param commandPacket
     * @param reader
     * @param query
     * @param projection
     * @param signature
     * @param message
     * @param handler
     * @return a command response
     * @throws InternalRequestException
     */
    public static CommandResponse selectQuery(InternalRequestHeader header, CommandPacket commandPacket,
            String reader, String query, List<String> projection, String signature, String message,
            ClientRequestHandlerInterface handler) throws InternalRequestException {
        if (Select.queryContainsEvil(query)) {
            return new CommandResponse(ResponseCode.OPERATION_NOT_SUPPORTED, GNSProtocol.BAD_RESPONSE.toString()
                    + " " + GNSProtocol.OPERATION_NOT_SUPPORTED.toString() + " Bad query operators in " + query);
        }
        JSONArray result;
        try {
            SelectRequestPacket packet = SelectRequestPacket.MakeQueryRequest(-1, reader, query, projection);
            result = executeSelectHelper(header, commandPacket, packet, reader, signature, message,
                    handler.getApp());
            if (result != null) {
                return new CommandResponse(ResponseCode.NO_ERROR, result.toString());
            }
        } catch (IOException | JSONException | FailedDBOperationException e) {
            // FIXME: why silently fail?
        }
        return new CommandResponse(ResponseCode.NO_ERROR, EMPTY_JSON_ARRAY_STRING);
    }

    /**
     * Sends a select request to the server to setup a context aware group guid and retrieve all the guids matching the query.
     *
     * @param header
     * @param commandPacket
     * @param reader
     * @param accountGuid
     * @param query
     * @param publicKey
     * @param interval - the refresh interval (queries made more quickly than this will get a cached value)
     * @param signature
     * @param message
     * @param handler
     * @return a command response
     * @throws InternalRequestException
     */
    public static CommandResponse selectGroupSetupQuery(InternalRequestHeader header, CommandPacket commandPacket,
            String reader, String accountGuid, String query, String publicKey, int interval, String signature,
            String message, ClientRequestHandlerInterface handler) throws InternalRequestException {
        String guid = SharedGuidUtils.createGuidStringFromBase64PublicKey(publicKey);
        //String guid = SharedGuidUtils.createGuidStringFromPublicKey(Base64.decode(publicKey));
        // Check to see if the guid doesn't exists and if so create it...
        if (AccountAccess.lookupGuidInfoAnywhere(header, guid, handler) == null) {
            // This code is similar to the code in AddGuid command except that we're not checking signatures... yet.
            // FIXME: This should probably include authentication
            GuidInfo accountGuidInfo;
            if ((accountGuidInfo = AccountAccess.lookupGuidInfoAnywhere(header, accountGuid, handler)) == null) {
                return new CommandResponse(ResponseCode.BAD_GUID_ERROR, GNSProtocol.BAD_RESPONSE.toString() + " "
                        + GNSProtocol.BAD_GUID.toString() + " " + accountGuid);
            }
            AccountInfo accountInfo = AccountAccess.lookupAccountInfoFromGuidAnywhere(header, accountGuid, handler);
            if (accountInfo == null) {
                return new CommandResponse(ResponseCode.BAD_ACCOUNT_ERROR, GNSProtocol.BAD_RESPONSE.toString() + " "
                        + GNSProtocol.BAD_ACCOUNT.toString() + " " + accountGuid);
            }
            if (!accountInfo.isVerified()) {
                return new CommandResponse(ResponseCode.VERIFICATION_ERROR, GNSProtocol.BAD_RESPONSE.toString()
                        + " " + GNSProtocol.VERIFICATION_ERROR.toString() + " Account not verified");
            } else if (accountInfo.getGuids().size() > Config
                    .getGlobalInt(GNSConfig.GNSC.ACCOUNT_GUID_MAX_SUBGUIDS)) {
                return new CommandResponse(ResponseCode.TOO_MANY_GUIDS_EXCEPTION,
                        GNSProtocol.BAD_RESPONSE.toString() + " " + GNSProtocol.TOO_MANY_GUIDS.toString());
            } else {
                // The alias (HRN) of the new guid is a hash of the query.
                String name = Base64.encodeToString(ShaOneHashFunction.getInstance().hash(query), false);
                CommandResponse groupGuidCreateresult = AccountAccess.addGuid(header, commandPacket, accountInfo,
                        accountGuidInfo, name, guid, publicKey, handler);
                // If there was a problem adding return that error response.
                if (!groupGuidCreateresult.getExceptionOrErrorCode().isOKResult()) {
                    return groupGuidCreateresult;
                }
            }
        }
        JSONArray result;

        try {
            SelectRequestPacket packet = SelectRequestPacket.MakeGroupSetupRequest(-1, reader, query, null, guid,
                    interval);
            result = executeSelectHelper(header, commandPacket, packet, reader, signature, message,
                    handler.getApp());
            if (result != null) {
                return new CommandResponse(ResponseCode.NO_ERROR, result.toString());
            }
        } catch (IOException | JSONException | FailedDBOperationException e) {
            LOGGER.log(Level.FINE, "Silently failing select for query: {0} field: {1} reader: {2} due to {3}",
                    new Object[] { guid, query, reader, e.getMessage() });
        }
        return new CommandResponse(ResponseCode.NO_ERROR, EMPTY_JSON_ARRAY_STRING);
    }

    /**
     * Sends a select request to the server to retrieve the members of a context aware group guid.
     *
     * @param header
     * @param commandPacket
     * @param reader
     * @param accountGuid - the guid (which should have been previously initialized using <code>selectGroupSetupQuery</code>
     * @param signature
     * @param message
     * @param handler
     * @return a command response
     * @throws InternalRequestException
     */
    public static CommandResponse selectGroupLookupQuery(InternalRequestHeader header, CommandPacket commandPacket,
            String reader, String accountGuid, String signature, String message,
            ClientRequestHandlerInterface handler) throws InternalRequestException {
        JSONArray result;
        try {
            SelectRequestPacket packet = SelectRequestPacket.MakeGroupLookupRequest(-1, reader, accountGuid);
            result = executeSelectHelper(header, commandPacket, packet, reader, signature, message,
                    handler.getApp());
            if (result != null) {
                return new CommandResponse(ResponseCode.NO_ERROR, result.toString());
            }
        } catch (IOException | JSONException | FailedDBOperationException e) {
            // FIXME: why silently fail?
        }
        return new CommandResponse(ResponseCode.NO_ERROR, EMPTY_JSON_ARRAY_STRING);
    }

    /**
     *
     * @param header
     * @param commandPacket
     * @param guid
     * @param field
     * @param fields
     * @param reader
     * @param signature
     * @param message
     * @param timestamp
     * @param app
     * @return the ResponseCode
     */
    public static ResponseCode signatureAndACLCheckForRead(InternalRequestHeader header,
            CommandPacket commandPacket, String guid, String field, List<String> fields, String reader,
            String signature, String message, Date timestamp, GNSApplicationInterface<String> app) {
        return signatureAndACLCheckForRead(header, commandPacket, guid, field, fields, reader, signature, message,
                timestamp, app, false);
    }

    /**
     *
     * @param header
     * @param commandPacket
     * @param guid
     * @param field
     * @param fields
     * @param reader
     * @param signature
     * @param message
     * @param timestamp
     * @param app
     * @param skipSigCheck
     * @return the ResponseCode
     */
    public static ResponseCode signatureAndACLCheckForRead(InternalRequestHeader header,
            CommandPacket commandPacket, String guid, String field, List<String> fields, String reader,
            String signature, String message, Date timestamp, GNSApplicationInterface<String> app,
            boolean skipSigCheck) {
        ResponseCode errorCode = ResponseCode.NO_ERROR;
        LOGGER.log(Level.FINE, "signatureAndACLCheckForRead guid: {0} field: {1} reader: {2}",
                new Object[] { guid, field, reader });
        try {
            assert (header != null);

            // note: reader can also be null here
            if (!header.verifyInternal() && !commandPacket.getCommandType().isMutualAuth()
                    && (field != null || fields != null)) {
                errorCode = NSAuthentication.signatureAndACLCheck(header, guid, field, fields, reader, signature,
                        message, MetaDataTypeName.READ_WHITELIST, app, skipSigCheck);
            } else {
                LOGGER.log(Level.FINEST, "reader={0}; internal={1} field={2}; fields={3};",
                        new Object[] { reader, header.verifyInternal(), field, fields });

                // internal and mutual auth commands don't need ACL checks
                if ((header.verifyInternal() && (GNSProtocol.INTERNAL_QUERIER.toString().equals(reader)))
                        || commandPacket.getCommandType().isMutualAuth()) {
                    return ResponseCode.NO_ERROR;
                }
                //Fixme: I'm guessing this case is for active code only.
                if (field != null) {
                    errorCode = NSAuthentication.aclCheck(header, guid, field, header.getQueryingGUID(),
                            MetaDataTypeName.READ_WHITELIST, app).getResponseCode();
                } else if (fields != null) {
                    for (String aField : fields) {
                        AclCheckResult aclResult = NSAuthentication.aclCheck(header, guid, aField,
                                header.getQueryingGUID(), MetaDataTypeName.READ_WHITELIST, app);
                        if (aclResult.getResponseCode().isExceptionOrError()) {
                            errorCode = aclResult.getResponseCode();
                        }
                    }
                }
            }
            // Check for stale commands.
            if (timestamp != null) {
                if (timestamp.before(DateUtils.addMinutes(new Date(),
                        -Config.getGlobalInt(GNSConfig.GNSC.STALE_COMMAND_INTERVAL_IN_MINUTES)))) {
                    errorCode = ResponseCode.STALE_COMMAND_VALUE;
                }
            }
        } catch (InvalidKeyException | InvalidKeySpecException | SignatureException | NoSuchAlgorithmException
                | FailedDBOperationException | UnsupportedEncodingException e) {
            errorCode = ResponseCode.SIGNATURE_ERROR;
        }
        return errorCode;
    }

}