package voldemort.rest;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import voldemort.store.CompositePutVoldemortRequest;
import voldemort.store.CompositeVersionedPutVoldemortRequest;
import voldemort.store.CompositeVoldemortRequest;
import voldemort.utils.ByteArray;
import voldemort.versioning.Versioned;
/**
* This class is used to parse an incoming put request. Parses and validates the
* REST Request and constructs a CompositeVoldemortRequestObject. Also Handles
* exceptions specific to put operation.
*/
public class RestPutRequestValidator extends RestRequestValidator {
private final boolean isVectorClockOptional;
private final Logger logger = Logger.getLogger(RestPutRequestValidator.class);
public RestPutRequestValidator(HttpRequest request,
MessageEvent messageEvent,
boolean isVectorClockOptional) {
super(request, messageEvent);
this.isVectorClockOptional = isVectorClockOptional;
}
@Override
public CompositeVoldemortRequest<ByteArray, byte[]> constructCompositeVoldemortRequestObject() {
CompositeVoldemortRequest<ByteArray, byte[]> requestObject = null;
if(parseAndValidateRequest()) {
parseValue();
if(this.parsedValue != null) {
// Check if we have a valid vector clock
if(this.parsedVectorClock == null
|| this.parsedVectorClock.getEntries().size() == 0) {
if(logger.isDebugEnabled()) {
debugLog("PUT", System.currentTimeMillis());
}
requestObject = new CompositePutVoldemortRequest<ByteArray, byte[]>(this.parsedKeys.get(0),
this.parsedValue,
this.parsedTimeoutInMs,
this.parsedRequestOriginTimeInMs,
this.parsedRoutingType);
} else {
if(logger.isDebugEnabled()) {
debugLog("PUT_VERSION", System.currentTimeMillis());
}
requestObject = new CompositeVersionedPutVoldemortRequest<ByteArray, byte[]>(this.parsedKeys.get(0),
new Versioned<byte[]>(this.parsedValue,
this.parsedVectorClock),
this.parsedTimeoutInMs,
this.parsedRequestOriginTimeInMs,
this.parsedRoutingType);
}
return requestObject;
} else {
logger.error("Error when parsing value. Value cannot be null.");
RestErrorHandler.writeErrorResponse(messageEvent,
HttpResponseStatus.BAD_REQUEST,
"Value cannot be null");
}
}
// Return null if request is not valid
return null;
}
/**
* Validations specific to PUT
*/
@Override
public boolean parseAndValidateRequest() {
boolean result = false;
if(!super.parseAndValidateRequest() || !hasVectorClock(this.isVectorClockOptional)
|| !hasContentLength() || !hasContentType()) {
result = false;
} else {
result = true;
}
return result;
}
/**
* Retrieves and validates the content length from the REST request.
*
* @return true if has content length
*/
protected boolean hasContentLength() {
boolean result = false;
String contentLength = this.request.getHeader(RestMessageHeaders.CONTENT_LENGTH);
if(contentLength != null) {
try {
Long.parseLong(contentLength);
result = true;
} catch(NumberFormatException nfe) {
logger.error("Exception when validating put request. Incorrect content length parameter. Cannot parse this to long: "
+ contentLength + ". Details: " + nfe.getMessage(),
nfe);
RestErrorHandler.writeErrorResponse(this.messageEvent,
HttpResponseStatus.BAD_REQUEST,
"Incorrect content length parameter. Cannot parse this to long: "
+ contentLength + ". Details: "
+ nfe.getMessage());
}
} else {
logger.error("Error when validating put request. Missing Content-Length header.");
RestErrorHandler.writeErrorResponse(this.messageEvent,
HttpResponseStatus.BAD_REQUEST,
"Missing Content-Length header");
}
return result;
}
/**
* Retrieves and validates the content type from the REST requests
*
* @return true if has content type.
*/
protected boolean hasContentType() {
boolean result = false;
if(this.request.getHeader(RestMessageHeaders.CONTENT_TYPE) != null) {
result = true;
} else {
logger.error("Error when validating put request. Missing Content-Type header.");
RestErrorHandler.writeErrorResponse(this.messageEvent,
HttpResponseStatus.BAD_REQUEST,
"Missing Content-Type header");
}
return result;
}
/**
* Retrieve the value from the REST request body.
*
* TODO: REST-Server value cannot be null ( null/empty string ?)
*/
private void parseValue() {
ChannelBuffer content = this.request.getContent();
this.parsedValue = new byte[content.capacity()];
content.readBytes(parsedValue);
}
}