//Dstl (c) Crown Copyright 2017
package uk.gov.dstl.baleen.core.web.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.slf4j.Logger;
import uk.gov.dstl.baleen.core.metrics.Metrics;
import uk.gov.dstl.baleen.core.metrics.MetricsFactory;
import uk.gov.dstl.baleen.core.web.security.WebPermission;
import com.codahale.metrics.Timer;
import com.codahale.metrics.Timer.Context;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.base.Strings;
import com.google.common.net.MediaType;
/**
* Basic class for JSON APIs.
*
*
*
*
*/
public abstract class AbstractApiServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final transient ObjectMapper mapper;
private final transient Logger logger;
private final transient Metrics metrics;
/**
* New instance.
*
* @param logger
* the logger to output events to
*
*/
public AbstractApiServlet(Logger logger, Metrics metrics) {
this.logger = logger;
this.metrics = metrics;
this.mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
}
/**
* New instance with default naming for metrics entries.
*
* @param logger
* @param clazz
*/
public AbstractApiServlet(Logger logger, Class<?> clazz) {
this(logger, MetricsFactory.getMetrics("web", clazz));
}
/**
* Get the JSON mapper.
*
* @return mapper (non-null)
*/
protected ObjectMapper getMapper() {
return mapper;
}
/**
* Provides the child servlet an opportunity to define the role permissions
* it requires.
*
* @return an array of allowable permissions
*/
public WebPermission[] getPermissions() {
return new WebPermission[] {};
}
protected boolean parametersPresent(String... params) {
for (String param : params) {
if (Strings.isNullOrEmpty(param)) {
return false;
}
}
return true;
}
/**
* Respond to the HTTP request .
*
* @param resp
* the response
* @param type
* the media type
* @param content
* to return
* @throws IOException
*/
protected void respond(HttpServletResponse resp, MediaType type, String content) throws IOException {
resp.setStatus(200);
resp.setContentType(type.toString());
resp.getWriter().write(content);
}
/**
* Sends a message when input arguments/parameters are bad.
*
* @param resp
* the response
* @throws IOException
*/
protected void respondWithBadArguments(HttpServletResponse resp) throws IOException {
respondWithError(resp, HttpStatus.BAD_REQUEST_400, "Required parameters not supplied");
}
/**
* Sends a message when the item requested by the call is not found.
*
* @param resp
* the response
* @throws IOException
*/
protected void respondWithNotFound(HttpServletResponse resp) throws IOException {
respondWithError(resp, HttpStatus.NOT_FOUND_404, "Item not found");
}
/**
* Sends a successful response and serialises with JSON.
*
* @param resp
* the response from the do* method.
* @param value
* the value to serialise.
* @throws IOException
*/
protected <T> void respondWithJson(HttpServletResponse resp, T value) throws IOException {
resp.setStatus(200);
resp.setContentType(MediaType.JSON_UTF_8.toString());
ServletOutputStream os = resp.getOutputStream();
mapper.writeValue(os, value);
}
/**
* Send an error signalling a problem with the request or its processing.
* The response should not be used after calling this function.
*
* @param resp
* the response from the do* method.
* @param statusCode
* the HTTP status code to return
* @param message
* the message accompanying the status code to add more
* description
* @throws IOException
*/
protected void respondWithError(HttpServletResponse resp, int statusCode, String message) throws IOException {
resp.sendError(statusCode, message);
}
/**
* Send an error signalling a problem with the request or its processing.
* The response should not be used after calling this function.
*
* @param resp
* the response from the do* method.
* @param statusCode
* the HTTP status code to return
*/
protected void respond(HttpServletResponse resp, int statusCode) {
resp.setStatus(statusCode);
}
@Override
protected final void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log(req);
Context context = getTimer(req).time();
try {
get(req, resp);
} finally {
context.close();
}
}
@Override
protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log(req);
Context context = getTimer(req).time();
try {
post(req, resp);
} finally {
context.close();
}
}
@Override
protected final void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log(req);
Context context = getTimer(req).time();
try {
put(req, resp);
} finally {
context.close();
}
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log(req);
Context context = getTimer(req).time();
try {
delete(req, resp);
} finally {
context.close();
}
}
private void log(HttpServletRequest req) {
logger.info("{} {}", req.getMethod(), req.getRequestURI());
}
private Timer getTimer(HttpServletRequest req) {
String path = String.format("%s:%s%s", req.getMethod(), req.getContextPath(), req.getServletPath());
return metrics.getTimer(path);
}
/**
* Called for a GET method, logging and metrics have already been captured.
*
* @param req
* as per HttpServlet
* @param resp
* as per HttpServlet
* @throws ServletException
* @throws IOException
*/
protected void get(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* Called for a POST method, logging and metrics have already been captured.
*
* @param req
* as per HttpServlet
* @param resp
* as per HttpServlet
* @throws ServletException
* @throws IOException
*/
protected void post(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* Called for a PUT method, logging and metrics have already been captured.
*
* @param req
* as per HttpServlet
* @param resp
* as per HttpServlet
* @throws ServletException
* @throws IOException
*/
protected void put(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
/**
* Called for a DELETE method, logging and metrics have already been
* captured.
*
* @param req
* as per HttpServlet
* @param resp
* as per HttpServlet
* @throws ServletException
* @throws IOException
*/
protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}