Должен ли один вызов .close() на HttpServletResponse.getOutputStream()/. GetWriter()?

Я не мог найти авторитетного ответа на этот вопрос с некоторыми Google. В сервлетах Java можно получить доступ к телу ответа через response.getOutputStream() или response.getWriter(). Должен ли один вызов .close() в этом потоке после того, как он был записан?

С одной стороны, существует блоховское увещевание всегда закрывать выходные потоки. С другой стороны, я не думаю, что в этом случае есть основной ресурс, который необходимо закрыть. Открытие/закрытие сокетов управляется на уровне HTTP, чтобы разрешить такие вещи, как постоянные соединения и т.д.

Ответ 1

Обычно вы не должны закрывать поток. Контейнер сервлета автоматически закрывает поток после завершения сервлета как часть жизненного цикла запроса сервлета.

Например, если вы закрыли поток, он не будет доступен, если вы внедрили Filter.

Сказав все это, если вы закрываете его, ничего плохого не произойдет, пока вы не будете пытаться использовать его снова.

EDIT: ссылка другого фильтра

EDIT2: adrian.tarau верен в том, что если вы хотите изменить ответ после того, как сервлет выполнил свою задачу, вы должны создать оболочку, расширяющую HttpServletResponseWrapper, и буферизировать вывод. Это необходимо для того, чтобы выход был напрямую передан клиенту, но также позволяет защитить, если сервлет закрывает поток, в соответствии с этой выдержкой (выделение мое):

Фильтр, который изменяет ответ, должен обычно захватывают ответ до него возвращается клиенту. Путь к сделайте это, чтобы передать сервлет, который генерирует ответ в режиме ожидания поток. Резервный поток предотвращает сервлет от закрытия оригинала поток ответов, когда он завершается, и позволяет фильтру изменять ответ сервлета.

Статья

Из этой официальной статьи Sun можно сделать вывод, что закрытие выходного потока сервлета является тем, что является обычным явлением, но не является обязательным.

Ответ 2

Общим правилом является следующее: если вы открыли поток, вы должны закрыть его. Если вы этого не сделали, вы не должны. Убедитесь, что код симметричен.

В случае HttpServletResponse он немного менее четкий, так как это не очевидно, если вызов getOutputStream() - это операция, которая открывает поток. Джавадок просто говорит, что это "Returns a ServletOutputStream"; аналогично для getWriter(). В любом случае, ясно, что HttpServletResponse "владеет" потоком/писателем, и он (или контейнер) отвечает за его повторное закрытие.

Итак, чтобы ответить на ваш вопрос - нет, вы не должны закрывать поток в этом случае. Контейнер должен это сделать, и если вы доберетесь до него, вы рискуете ввести тонкие ошибки в свое приложение.

Ответ 3

Если есть вероятность, что фильтр может быть вызван на "включенном" ресурсе, вам определенно не следует закрывать поток. Это приведет к сбою включенного ресурса с исключением "закрытого потока".

Ответ 4

Вы должны закрыть поток, код становится более чистым, так как вы вызываете getOutputStream(), и поток не передается вам как параметр, когда обычно вы просто используете его и не пытаетесь его закрыть. API-интерфейс Servlet не утверждает, что, если выходной поток может быть закрыт или не должен быть закрыт, в этом случае вы можете безопасно закрыть поток, любой контейнер там позаботится о закрытии потока, если он не был закрыт сервлетом.

Вот метод close() в Jetty, они закрывают поток, если он не закрыт.

public void close() throws IOException
    {
        if (_closed)
            return;

        if (!isIncluding() && !_generator.isCommitted())
            commitResponse(HttpGenerator.LAST);
        else
            flushResponse();

        super.close();
    }

Также как разработчик фильтра вы не должны предполагать, что OutputStream не закрыт, вы всегда должны передавать другой OutputStream, если хотите изменить содержимое после того, как сервлет выполнил свою работу.

EDIT: Я всегда закрываю поток, и у меня не было проблем с Tomcat/Jetty. Я не думаю, что у вас возникнут проблемы с контейнером, старым или новым.

Ответ 5

Другой аргумент против закрытия OutputStream. Посмотрите на этот сервлет. Это исключает. Исключение отображается в файле web.xml с ошибкой JSP:

package ser;

import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet(name = "Erroneous", urlPatterns = {"/Erroneous"})
public class Erroneous extends HttpServlet {

  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setContentType("text/html;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    try {
      throw new IOException("An error");
    } finally {
//      out.close();
    }
  }
}

Файл web.xml содержит:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <error-page>
        <exception-type>java.io.IOException</exception-type>
        <location>/error.jsp</location>
    </error-page>
</web-app>

И error.jsp:

<%@page contentType="text/html" pageEncoding="UTF-8" isErrorPage="true"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Error Page</title>
    </head>
    <body>
        <h1><%= exception.getMessage()%></h1>
    </body>
</html>

Когда вы загружаете /Erroneous в браузере, вы видите страницу с ошибкой, отображающую "Ошибка". Но если вы прокомментируете строку out.close() в приведенном выше сервлете, повторно разверните приложение и перезагрузите /Erroneous, вы ничего не увидите в браузере. Я не знаю, что на самом деле происходит, но я думаю, что out.close() предотвращает обработку ошибок.

Протестировано с Tomcat 7.0.50, Java EE 6 с использованием Netbeans 7.4.