/*
* Copyright (C) 2010-2012 Stichting Akvo (Akvo Foundation)
*
* This file is part of Akvo FLOW.
*
* Akvo FLOW is free software: you can redistribute it and modify it under the terms of
* the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
* either version 3 of the License or any later version.
*
* Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License included below for more details.
*
* The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
*/
package com.gallatinsystems.framework.rest;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.servlet.http.HttpServletRequest;
import com.gallatinsystems.framework.rest.exception.RestValidationException;
/**
* base class for all rest api requests. It handles populating the common api attributes (action,
* apiKey, start/endRow, maxResults). All apis do NOT need to take these params, but if they do, the
* param names defined in this class should be used so they can be handled automatically.
*
* @author Christopher Fagiani
*/
public abstract class RestRequest implements Serializable {
private static final long serialVersionUID = -8356057661356968219L;
public static final String ACTION_PARAM = "action";
private static final String STARTROW_PARAM = "startRow";
private static final String ENDROW_PARAM = "endRow";
public static final String CURSOR_PARAM = "cursor";
private static final String DESIRED_RESULTS_PARAM = "maxResults";
private static final String NULL_STRING = "null";
public static final String API_KEY_PARAM = "k";
public static final String HASH_PARAM = "h";
public static final String TIMESTAMP_PARAM = "ts";
private List<RestError> validationErrorList;
private int startRow;
private int endRow;
private int desiredResults;
private String action;
private String apiKey;
private String hash;
private Date timestamp;
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
private String cursor;
/**
* populates the common fields and then dispatches ot the populateFields abstract method for
* subclass specific handling.
*
* @param servletRequest
* @throws Exception
*/
public void populateFromHttpRequest(HttpServletRequest servletRequest)
throws Exception {
setAction(servletRequest.getParameter(RestRequest.ACTION_PARAM));
setApiKey(servletRequest.getParameter(RestRequest.API_KEY_PARAM));
setHash(servletRequest.getParameter(RestRequest.HASH_PARAM));
if (servletRequest.getParameter(RestRequest.TIMESTAMP_PARAM) != null) {
try {
DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
setTimestamp(df.parse(servletRequest.getParameter(RestRequest.TIMESTAMP_PARAM)));
} catch (Exception e) {
addError(new RestError(RestError.BAD_DATATYPE_CODE,
RestError.BAD_DATATYPE_MESSAGE, TIMESTAMP_PARAM
+ " must be an w3c date"));
}
}
cursor = servletRequest.getParameter(CURSOR_PARAM);
if (cursor != null) {
if (NULL_STRING.equalsIgnoreCase(cursor.trim())
|| cursor.trim().length() == 0) {
cursor = null;
}
}
setStartRow(stringToInt(servletRequest
.getParameter(RestRequest.STARTROW_PARAM)));
setEndRow(stringToInt(servletRequest
.getParameter(RestRequest.ENDROW_PARAM)));
setDesiredResults(stringToInt(servletRequest
.getParameter(RestRequest.DESIRED_RESULTS_PARAM)));
populateFields(servletRequest);
}
public String getCursor() {
return cursor;
}
public void setCursor(String cursor) {
this.cursor = cursor;
}
public List<RestError> getValidationErrorList() {
return validationErrorList;
}
public int getStartRow() {
return startRow;
}
public void setStartRow(int startRow) {
this.startRow = startRow;
}
public int getEndRow() {
return endRow;
}
public void setEndRow(int endRow) {
this.endRow = endRow;
}
public int getDesiredResults() {
return desiredResults;
}
public void setDesiredResults(int desiredResults) {
this.desiredResults = desiredResults;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
private int stringToInt(String val) {
int intVal = -1;
if (val != null) {
intVal = Integer.parseInt(val);
}
return intVal;
}
/**
* adds a RestError to the internal validationError list
*
* @param err
*/
protected void addError(RestError err) {
if (validationErrorList == null) {
validationErrorList = new ArrayList<RestError>();
}
validationErrorList.add(err);
}
/**
* subclasses should implement this method such that it reads all the subclass-specific values
* from the HTTP request and sets the corresponding fields within the subclass with the values.
*
* @param req
* @throws Exception
*/
protected abstract void populateFields(HttpServletRequest req)
throws Exception;
/**
* populates any errors in the subclass-specific fields
*/
protected abstract void populateErrors();
/**
* validates the request by calling populateErrors to perform subclass-specific validation.
*
* @throws RestValidationException
*/
public void validate() throws RestValidationException {
populateErrors();
if (validationErrorList != null && validationErrorList.size() > 0) {
throw new RestValidationException(validationErrorList,
"Validation error", null);
}
}
/**
* Convenience method to parse a string value as a Long and to add a new BAD_DATATYPE error in
* the event that its unparseable
*
* @param val
* @param field
* @return
*/
protected Long parseLong(String val, String field) {
Long result = null;
if (val != null && val.trim().length() > 0) {
try {
result = Long.parseLong(val);
} catch (Exception e) {
addError(new RestError(RestError.BAD_DATATYPE_CODE,
RestError.BAD_DATATYPE_MESSAGE, field
+ " must be an integer"));
}
}
return result;
}
}