Package voldemort.rest

Source Code of voldemort.rest.RestRequestValidator

package voldemort.rest;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;

import voldemort.VoldemortException;
import voldemort.server.RequestRoutingType;
import voldemort.store.CompositeVoldemortRequest;
import voldemort.utils.ByteArray;
import voldemort.versioning.VectorClock;

/**
* Super class to parse, validate REST requests
*/

public abstract class RestRequestValidator {

    protected CompositeVoldemortRequest<ByteArray, byte[]> requestObject;
    protected String storeName = null;
    protected List<ByteArray> parsedKeys;
    protected byte[] parsedValue = null;
    protected VectorClock parsedVectorClock = null;
    protected long parsedTimeoutInMs;
    protected byte parsedOperationType;
    protected long parsedRequestOriginTimeInMs;
    protected RequestRoutingType parsedRoutingType = null;
    protected MessageEvent messageEvent;
    protected HttpRequest request;
    protected final Logger logger = Logger.getLogger(getClass());

    public RestRequestValidator(HttpRequest request, MessageEvent messageEvent) {
        this.request = request;
        this.messageEvent = messageEvent;
        this.parsedRoutingType = RequestRoutingType.NORMAL;
    }

    public abstract CompositeVoldemortRequest<ByteArray, byte[]> constructCompositeVoldemortRequestObject();

    /**
     * Validations common for all operations are done here
     *
     * @return true if request is valid else false
     */
    protected boolean parseAndValidateRequest() {
        if(!hasKey() || !hasTimeOutHeader() || !hasTimeStampHeader() || !isStoreValid()) {
            return false;
        }

        // Retrieve the routing code from the header
        parseRoutingCodeHeader();

        return true;
    }

    /**
     * Retrieve and validate the timeout value from the REST request.
     * "X_VOLD_REQUEST_TIMEOUT_MS" is the timeout header.
     *
     * @return true if present, false if missing
     */
    protected boolean hasTimeOutHeader() {

        boolean result = false;
        String timeoutValStr = this.request.getHeader(RestMessageHeaders.X_VOLD_REQUEST_TIMEOUT_MS);
        if(timeoutValStr != null) {
            try {
                this.parsedTimeoutInMs = Long.parseLong(timeoutValStr);
                if(this.parsedTimeoutInMs < 0) {
                    RestErrorHandler.writeErrorResponse(messageEvent,
                                                        HttpResponseStatus.BAD_REQUEST,
                                                        "Time out cannot be negative ");

                } else {
                    result = true;
                }
            } catch(NumberFormatException nfe) {
                logger.error("Exception when validating request. Incorrect timeout parameter. Cannot parse this to long: "
                                     + timeoutValStr,
                             nfe);
                RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                    HttpResponseStatus.BAD_REQUEST,
                                                    "Incorrect timeout parameter. Cannot parse this to long: "
                                                            + timeoutValStr);
            }
        } else {
            logger.error("Error when validating request. Missing timeout parameter.");
            RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                HttpResponseStatus.BAD_REQUEST,
                                                "Missing timeout parameter.");
        }
        return result;
    }

    /**
     * Retrieve the routing type value from the REST request.
     * "X_VOLD_ROUTING_TYPE_CODE" is the routing type header.
     *
     * By default, the routing code is set to NORMAL
     *
     * TODO REST-Server 1. Change the header name to a better name. 2. Assumes
     * that integer is passed in the header
     *
     */
    protected void parseRoutingCodeHeader() {

        String rtCode = this.request.getHeader(RestMessageHeaders.X_VOLD_ROUTING_TYPE_CODE);
        if(rtCode != null) {
            try {
                int routingTypeCode = Integer.parseInt(rtCode);
                this.parsedRoutingType = RequestRoutingType.getRequestRoutingType(routingTypeCode);
            } catch(NumberFormatException nfe) {
                logger.error("Exception when validating request. Incorrect routing type parameter. Cannot parse this to long: "
                                     + rtCode,
                             nfe);
                RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                    HttpResponseStatus.BAD_REQUEST,
                                                    "Incorrect routing type parameter. Cannot parse this to long: "
                                                            + rtCode);
            } catch(VoldemortException ve) {
                logger.error("Exception when validating request. Incorrect routing type code: "
                             + rtCode, ve);
                RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                    HttpResponseStatus.BAD_REQUEST,
                                                    "Incorrect routing type code: " + rtCode);
            }
        }
    }

    /**
     * Retrieve and validate the timestamp value from the REST request.
     * "X_VOLD_REQUEST_ORIGIN_TIME_MS" is timestamp header
     *
     * TODO REST-Server 1. Change Time stamp header to a better name.
     *
     * @return true if present, false if missing
     */
    protected boolean hasTimeStampHeader() {
        String originTime = request.getHeader(RestMessageHeaders.X_VOLD_REQUEST_ORIGIN_TIME_MS);
        boolean result = false;
        if(originTime != null) {
            try {
                // TODO: remove the originTime field from request header,
                // because coordinator should not accept the request origin time
                // from the client.. In this commit, we only changed
                // "this.parsedRequestOriginTimeInMs" from
                // "Long.parseLong(originTime)" to current system time,
                // The reason that we did not remove the field from request
                // header right now, is because this commit is a quick fix for
                // internal performance test to be available as soon as
                // possible.

                this.parsedRequestOriginTimeInMs = System.currentTimeMillis();
                if(this.parsedRequestOriginTimeInMs < 0) {
                    RestErrorHandler.writeErrorResponse(messageEvent,
                                                        HttpResponseStatus.BAD_REQUEST,
                                                        "Origin time cannot be negative ");

                } else {
                    result = true;
                }
            } catch(NumberFormatException nfe) {
                logger.error("Exception when validating request. Incorrect origin time parameter. Cannot parse this to long: "
                                     + originTime,
                             nfe);
                RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                    HttpResponseStatus.BAD_REQUEST,
                                                    "Incorrect origin time parameter. Cannot parse this to long: "
                                                            + originTime);
            }
        } else {
            logger.error("Error when validating request. Missing origin time parameter.");
            RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                HttpResponseStatus.BAD_REQUEST,
                                                "Missing origin time parameter.");
        }
        return result;
    }

    /**
     * Retrieve and validate vector clock value from the REST request.
     * "X_VOLD_VECTOR_CLOCK" is the vector clock header.
     *
     * @return true if present, false if missing
     */
    protected boolean hasVectorClock(boolean isVectorClockOptional) {
        boolean result = false;

        String vectorClockHeader = this.request.getHeader(RestMessageHeaders.X_VOLD_VECTOR_CLOCK);
        if(vectorClockHeader != null) {
            ObjectMapper mapper = new ObjectMapper();
            try {
                VectorClockWrapper vcWrapper = mapper.readValue(vectorClockHeader,
                                                                VectorClockWrapper.class);
                this.parsedVectorClock = new VectorClock(vcWrapper.getVersions(),
                                                         vcWrapper.getTimestamp());
                result = true;
            } catch(Exception e) {
                logger.error("Exception while parsing and constructing vector clock", e);
                RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                    HttpResponseStatus.BAD_REQUEST,
                                                    "Invalid Vector Clock");
            }
        } else if(!isVectorClockOptional) {
            logger.error("Error when validating request. Missing Vector Clock");
            RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                HttpResponseStatus.BAD_REQUEST,
                                                "Missing Vector Clock");
        } else {
            result = true;
        }

        return result;
    }

    /**
     * Retrieve and validate the key from the REST request.
     *
     * @return true if present, false if missing
     */
    protected boolean hasKey() {
        boolean result = false;
        String requestURI = this.request.getUri();
        parseKeys(requestURI);

        if(this.parsedKeys != null) {
            result = true;
        } else {
            logger.error("Error when validating request. No key specified.");
            RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                HttpResponseStatus.BAD_REQUEST,
                                                "Error: No key specified !");
        }
        return result;
    }

    /**
     * Method to read a key (or keys) present in the HTTP request URI. The URI
     * must be of the format /<store_name>/<key>[,<key>,...]
     *
     * @param requestURI The URI of the HTTP request
     */
    protected void parseKeys(String requestURI) {

        this.parsedKeys = null;
        String[] parts = requestURI.split("/");
        if(parts.length > 2) {
            String base64KeyList = parts[2];
            this.parsedKeys = new ArrayList<ByteArray>();

            if(!base64KeyList.contains(",")) {
                String rawKey = base64KeyList.trim();
                this.parsedKeys.add(new ByteArray(RestUtils.decodeVoldemortKey(rawKey)));
            } else {
                String[] base64KeyArray = base64KeyList.split(",");
                for(String base64Key: base64KeyArray) {
                    String rawKey = base64Key.trim();
                    this.parsedKeys.add(new ByteArray(RestUtils.decodeVoldemortKey(rawKey)));
                }
            }
        }
    }

    /**
     * Retrieve and validate store name from the REST request.
     *
     * @return true if valid, false otherwise
     */

    protected boolean isStoreValid() {
        boolean result = false;
        String requestURI = this.request.getUri();
        this.storeName = parseStoreName(requestURI);
        if(storeName != null) {
            result = true;
        } else {
            logger.error("Error when validatig request. Missing store name.");
            RestErrorHandler.writeErrorResponse(this.messageEvent,
                                                HttpResponseStatus.BAD_REQUEST,
                                                "Missing store name. Critical error.");
        }
        return result;
    }

    /**
     * Parses the store name HTTP request URI. The URI must be of the format
     * /<store_name>/<key>[,<key>,...]
     *
     * @param requestURI
     * @return a String representing store name
     */
    protected String parseStoreName(String requestURI) {
        String storeName = null;
        String[] parts = requestURI.split("/");
        if(parts.length > 1) {
            storeName = parts[1];
        }
        return storeName;
    }

    public String getStoreName() {
        return this.storeName;
    }

    public RequestRoutingType getParsedRoutingType() {
        return parsedRoutingType;
    }

    /**
     * Prints a debug log message that details the time taken for the Http
     * request to be parsed by the coordinator
     *
     * @param operationType
     * @param receivedTimeInMs
     */
    protected void debugLog(String operationType, Long receivedTimeInMs) {
        long durationInMs = receivedTimeInMs - (this.parsedRequestOriginTimeInMs);
        int numVectorClockEntries = (this.parsedVectorClock == null ? 0
                                                                   : this.parsedVectorClock.getVersionMap()
                                                                                           .size());
        logger.debug("Received a new request. Operation type: " + operationType + " , Key(s): "
                     + keysHexString(this.parsedKeys) + " , Store: " + this.storeName
                     + " , Origin time (in ms): " + (this.parsedRequestOriginTimeInMs)
                     + " , Request received at time(in ms): " + receivedTimeInMs
                     + " , Num vector clock entries: " + numVectorClockEntries
                     + " , Duration from RESTClient to CoordinatorRestRequestValidator(in ms): "
                     + durationInMs);

    }

    protected String keysHexString(List<ByteArray> keys) {
        return RestUtils.getKeysHexString(keys.iterator());
    }

}
TOP

Related Classes of voldemort.rest.RestRequestValidator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.