package com.google.step.coffee;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.Expose;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JsonServlet extends HttpServlet {
  protected static class HttpError extends Exception {
    @Expose private int errorCode;
    @Expose private String message;

    public HttpError(int errorCode, String message) {
      this.errorCode = errorCode;
      this.message = message;
    }

    public int getErrorCode() {
      return errorCode;
    }

    public String getMessage() {
      return message;
    }
  }

  protected interface JsonHttpHandler {
    Object handle(HttpServletRequest request) throws IOException, HttpError;
  }

  private static String stringify(Object object, boolean onlyExposed) {
    GsonBuilder builder = new GsonBuilder();
    builder.disableHtmlEscaping();
    if (onlyExposed) {
      builder.excludeFieldsWithoutExposeAnnotation();
    }
    Gson gson = builder.create();

    return gson.toJson(object);
  }

  public Object get(HttpServletRequest request) throws IOException, HttpError {
    throw new HttpError(
        HttpServletResponse.SC_METHOD_NOT_ALLOWED,
        "GET method is not allowed for this endpoint"
    );
  }

  public Object post(HttpServletRequest request) throws IOException, HttpError {
    throw new HttpError(
        HttpServletResponse.SC_METHOD_NOT_ALLOWED,
        "POST method is not allowed for this endpoint"
    );
  }

  private void handle(HttpServletRequest request, HttpServletResponse response, JsonHttpHandler handler) throws IOException {
    response.setCharacterEncoding("UTF-8");
    response.setContentType("text/json");

    try {
      Object jsonResponse = handler.handle(request);
      response.getWriter().write(stringify(jsonResponse, false));
    } catch (HttpError httpError) {
      response.setStatus(httpError.getErrorCode());
      response.getWriter().write(stringify(httpError, true));
    }
  }

  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    handle(request, response, this::get);
  }

  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
    handle(request, response, this::post);
  }
}

@@ -296,16 +296,477 @@
 ption;%0A%0A
+/**%0A *%0A * Provides an abstract class for JSON endpoints.%0A * Users of this class are expected to override %3Ccode%3Eget%3C/code%3E and %3Ccode%3Epost%3C/code%3E%0A * methods to either return a Gson-serializable %3Ccode%3EObject%3C/code%3E or to throw an exception.%0A *%0A * If a method throws %3Ccode%3EHttpError%3C/code%3E an appropriate HTTP error code will be%0A * set, and a JSON response in format of %3Ccode%3E%7B%22errorCode%22: ERROR_CODE, %22message%22: MESSAGE%7D%3C/code%3E%0A * will be returned.%0A *%0A*/%0Aabstract 
 public c

Add a short javadoc for JsonServlet