void doPost(final HttpServletRequest req, final HttpServletResponse resp) {
    try {
      // Parse out the required API versions.
      final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req);

      // Prepare response.
      prepareResponse(req, resp);

      // Validate request.
      preprocessRequest(req);
      rejectIfNoneMatch(req);
      rejectIfMatch(req);

      final Map<String, String[]> parameters = req.getParameterMap();
      final String action = asSingleValue(PARAM_ACTION, getParameter(req, PARAM_ACTION));
      if (action.equalsIgnoreCase(ACTION_ID_CREATE)) {
        final JsonValue content = getJsonContent(req);
        final CreateRequest request = Requests.newCreateRequest(getResourceName(req), content);
        for (final Map.Entry<String, String[]> p : parameters.entrySet()) {
          final String name = p.getKey();
          final String[] values = p.getValue();
          if (parseCommonParameter(name, values, request)) {
            continue;
          } else if (name.equalsIgnoreCase(PARAM_ACTION)) {
            // Ignore - already handled.
          } else if (HttpUtils.isMultiPartRequest(req.getContentType())) {
            // Ignore - multipart content adds form parts to the parameter set
          } else {
            request.setAdditionalParameter(name, asSingleValue(name, values));
          }
        }
        doRequest(req, resp, acceptVersion, request);
      } else {
        // Action request.
        final JsonValue content = getJsonActionContent(req);
        final ActionRequest request =
            Requests.newActionRequest(getResourceName(req), action).setContent(content);
        for (final Map.Entry<String, String[]> p : parameters.entrySet()) {
          final String name = p.getKey();
          final String[] values = p.getValue();
          if (parseCommonParameter(name, values, request)) {
            continue;
          } else if (name.equalsIgnoreCase(PARAM_ACTION)) {
            // Ignore - already handled.
          } else if (HttpUtils.isMultiPartRequest(req.getContentType())) {
            // Ignore - multipart content adds form parts to the parameter set
          } else {
            request.setAdditionalParameter(name, asSingleValue(name, values));
          }
        }
        doRequest(req, resp, acceptVersion, request);
      }
    } catch (final Exception e) {
      fail(req, resp, e);
    }
  }
  private void preprocessRequest(final HttpServletRequest req) throws ResourceException {
    // TODO: check Accept (including charset parameter) and Accept-Charset headers

    // Check content-type.
    final String contentType = req.getContentType();
    if (!req.getMethod().equalsIgnoreCase(HttpUtils.METHOD_GET)
        && contentType != null
        && !CONTENT_TYPE_REGEX.matcher(contentType).matches()
        && !HttpUtils.isMultiPartRequest(contentType)) {
      // TODO: i18n
      throw new BadRequestException(
          "The request could not be processed because it specified the content-type '"
              + req.getContentType()
              + "' when only the content-type '"
              + MIME_TYPE_APPLICATION_JSON
              + "' and '"
              + MIME_TYPE_MULTIPART_FORM_DATA
              + "' are supported");
    }

    if (req.getHeader("If-Modified-Since") != null) {
      // TODO: i18n
      throw new ConflictException("Header If-Modified-Since not supported");
    }

    if (req.getHeader("If-Unmodified-Since") != null) {
      // TODO: i18n
      throw new ConflictException("Header If-Unmodified-Since not supported");
    }
  }
  void doPatch(final HttpServletRequest req, final HttpServletResponse resp) {
    try {
      // Parse out the required API versions.
      final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req);

      // Prepare response.
      prepareResponse(req, resp);

      // Validate request.
      preprocessRequest(req);
      if (req.getHeader(HEADER_IF_NONE_MATCH) != null) {
        // FIXME: i18n
        throw new PreconditionFailedException(
            "Use of If-None-Match not supported for PATCH requests");
      }

      final Map<String, String[]> parameters = req.getParameterMap();
      final PatchRequest request =
          Requests.newPatchRequest(getResourceName(req)).setRevision(getIfMatch(req));
      request.getPatchOperations().addAll(getJsonPatchContent(req));
      for (final Map.Entry<String, String[]> p : parameters.entrySet()) {
        final String name = p.getKey();
        final String[] values = p.getValue();
        if (HttpUtils.isMultiPartRequest(req.getContentType())) {
          // Ignore - multipart content adds form parts to the parameter set
        } else if (parseCommonParameter(name, values, request)) {
          continue;
        } else {
          request.setAdditionalParameter(name, asSingleValue(name, values));
        }
      }
      doRequest(req, resp, acceptVersion, request);
    } catch (final Exception e) {
      fail(req, resp, e);
    }
  }
  void doPut(final HttpServletRequest req, final HttpServletResponse resp) {
    try {
      // Parse out the required API versions.
      final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req);

      // Prepare response.
      prepareResponse(req, resp);

      // Validate request.
      preprocessRequest(req);

      if (req.getHeader(HEADER_IF_MATCH) != null && req.getHeader(HEADER_IF_NONE_MATCH) != null) {
        // FIXME: i18n
        throw new PreconditionFailedException(
            "Simultaneous use of If-Match and If-None-Match not " + "supported for PUT requests");
      }

      final Map<String, String[]> parameters = req.getParameterMap();
      final JsonValue content = getJsonContent(req);

      final String rev = getIfNoneMatch(req);
      if (ETAG_ANY.equals(rev)) {
        // This is a create with a user provided resource ID: split the
        // path into the parent resource name and resource ID.
        final String resourceName = getResourceName(req);
        final int i = resourceName.lastIndexOf('/');
        final CreateRequest request;
        if (resourceName.isEmpty()) {
          // FIXME: i18n.
          throw new BadRequestException("No new resource ID in HTTP PUT request");
        } else if (i < 0) {
          // We have a pathInfo of the form "{id}"
          request = Requests.newCreateRequest(EMPTY_STRING, content);
          request.setNewResourceId(resourceName);
        } else {
          // We have a pathInfo of the form "{container}/{id}"
          request = Requests.newCreateRequest(resourceName.substring(0, i), content);
          request.setNewResourceId(resourceName.substring(i + 1));
        }
        for (final Map.Entry<String, String[]> p : parameters.entrySet()) {
          final String name = p.getKey();
          final String[] values = p.getValue();
          if (HttpUtils.isMultiPartRequest(req.getContentType())) {
            // Ignore - multipart content adds form parts to the parameter set
          } else if (parseCommonParameter(name, values, request)) {
            continue;
          } else {
            request.setAdditionalParameter(name, asSingleValue(name, values));
          }
        }
        doRequest(req, resp, acceptVersion, request);
      } else {
        final UpdateRequest request =
            Requests.newUpdateRequest(getResourceName(req), content).setRevision(getIfMatch(req));
        for (final Map.Entry<String, String[]> p : parameters.entrySet()) {
          final String name = p.getKey();
          final String[] values = p.getValue();
          if (HttpUtils.isMultiPartRequest(req.getContentType())) {
            // Ignore - multipart content adds form parts to the parameter set
          } else if (parseCommonParameter(name, values, request)) {
            continue;
          } else {
            request.setAdditionalParameter(name, asSingleValue(name, values));
          }
        }
        doRequest(req, resp, acceptVersion, request);
      }
    } catch (final Exception e) {
      fail(req, resp, e);
    }
  }