1
votes

I am writing a REST application using Tomcat and Spring WebMVC.

I want to signal errors to my client using HTTP status codes along with some XML payload that contains more information about what went wrong.

To catch all errors regardless of where they occur, I have written a Filter which wraps the response and overrides the sendError() method:

private static final class GenericErrorResponseWrapper 
extends HttpServletResponseWrapper   
{
  @Override
  public void sendError(int sc, String msg) throws IOException {
    final HttpServletResponse wrappedResponse = (HttpServletResponse) getResponse();
    wrappedResponse.setStatus(sc, msg);
    wrappedResponse.setContentType("application/xml");
    PrintWriter writer = wrappedResponse.getWriter();
    try {
      SimpleXmlWriter xmlWriter = SimpleXmlWriterWrapper.newInstance(writer);
      xmlWriter.writeStartElement("ns2", "genericError")
      .writeAttribute("xmlns:ns2", "http://mynamespace")
      .writeCharacters(msg)
      .writeEndDocument().flush();
      writer.flush();
      wrappedResponse.flushBuffer();
    } finally {
      writer.close();
    }
  }
}

This implementation has two problems:

  1. It generates a deprecation warning in Eclipse, since HttpServletResponse.setStatus(sc, msg) is deprecated.
  2. The HTTP response header generated by Tomcat is not correct, it starts with the first line "HTTP/1.1 500 OK". 500 is correct, but instead of OK the reason phrase should be "Internal Server Error".

How can I implement my filter so that it does the right thing and is free of deprecation warnings? Both alternatives named in the Javadoc are not usable for me:

  • sendError(sc, msg) is not usable, since it commits the response body and I can't write XML payload any more
  • setStatus(sc) with just the error code is theoretically usable, but it also creates the hardcoded "OK" string in the first line of the response header.
2
The javadoc explains why the method is deprecated, and which method you should use instead. Read it.JB Nizet
Unfortunately this doesn't help my, as I want to write my own response XML payload instead of the default tomcat HTML error page.Bastian Voigt
@JB Nizet: You should read the Javadoc. There is no replacement for setStatus(int sc, String msg) with the same functionality.jarnbjo
@jarnbo You should read it yourself. I quote: "As of version 2.1, due to ambiguous meaning of the message parameter. To set a status code use setStatus(int), to send an error with a description use sendError(int, String). Sets the status code and message for this response."user207421
@EJP: And which of the two methods do what Bastian is asking for? He even explained in his question why neither of the suggested replacement methods is suitable.jarnbjo

2 Answers

4
votes

There is unfortunately no way to avoid the deprecation warning. As you already mention yourself, the two alternatives which are referred to in the API documentation do not cover the same functionality. You may of course annotate your method with @SuppressWarnings("deprecation") to indicate that the usage of the deprecated method is intended.

The other thing, that Tomcat does not use your message string, even if one is provided, is a configuration issue. For some strange reason, Tomcat will by default ignore the provided message string and use a default error message based on the passed return code. You must set the system property org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER to true to force Tomcat to use your provided error message instead. More details on this can be found in the Tomcat documentation.

0
votes

As an alternative answer - you could first write the XML payload, without calling flush/flushBuffer, and only after that do sendError(int, String), which would flush the buffer.