¿Se debe llamar a .close () en HttpServletResponse.getOutputStream () /. GetWriter ()?

96

En Java Servlets, se puede acceder al cuerpo de la respuesta a través de response.getOutputStream()o response.getWriter(). ¿Debería recurrirse .close()a esto OutputStreamdespués de que se haya escrito?

Por un lado, está la exhortación blochiana a cerrar siempre OutputStream. Por otro lado, no creo que en este caso haya un recurso subyacente que deba cerrarse. La apertura / cierre de sockets se gestiona a nivel HTTP, para permitir cosas como conexiones persistentes y demás.

Steven Huwig
fuente
No está invitado a adivinar si hay un recurso subyacente para cerrar. Si el implementador lo cree, o más bien lo sabe, proporcionará un close()que no hace nada. Lo que debe hacer es cerrar todos los recursos que se puedan cerrar.
Marqués de Lorne
1
¿Incluso si su código no lo abrió? No lo creo ...
Steven Huwig

Respuestas:

93

Normalmente no deberías cerrar la corriente. El contenedor de servlets cerrará automáticamente la secuencia después de que el servlet termine de ejecutarse como parte del ciclo de vida de la solicitud del servlet.

Por ejemplo, si cerrara la transmisión, no estaría disponible si implementara un Filtro .

Habiendo dicho todo eso, si lo cierras, no sucederá nada malo mientras no intentes volver a usarlo.

EDITAR: otro enlace de filtro

EDIT2: adrian.tarau tiene razón en que si desea alterar la respuesta después de que el servlet haya hecho su trabajo, debe crear un contenedor que extienda HttpServletResponseWrapper y almacenar en búfer la salida. Esto es para evitar que la salida vaya directamente al cliente, pero también le permite proteger si el servlet cierra la transmisión, según este extracto (el énfasis es mío):

Un filtro que modifica una respuesta generalmente debe capturar la respuesta antes de que se devuelva al cliente. La forma de hacer esto es pasar el servlet que genera la respuesta a un flujo de suplencia. La secuencia suplementaria evita que el servlet cierre la secuencia de respuesta original cuando se completa y permite que el filtro modifique la respuesta del servlet.

Artículo

Se puede inferir de ese artículo oficial de Sun que cerrar el OutputStreamdesde un servlet es algo que ocurre normalmente, pero no es obligatorio.

Nemi
fuente
2
Esto es correcto. Una cosa a tener en cuenta es que, en algunos casos, es posible que deba vaciar la corriente, y eso está perfectamente permitido.
Toluju
1
Hay otro efecto secundario de cerrar el escritor. Tampoco podrá configurar el código de estado a través de response.setStatus después de que se haya cerrado.
che javara
1
Siga este consejo. Te ahorrará mucho dolor. Tampoco enjuagaría () a menos que sepa por qué lo está haciendo; debe dejar que el contenedor maneje el almacenamiento en búfer.
Hal50000
75

La regla general de ellos es la siguiente: si abrió la secuencia, debe cerrarla. Si no lo hizo, no debería. Asegúrate de que el código sea simétrico.

En el caso de HttpServletResponse, es un poco menos claro, ya que no es obvio si llamar getOutputStream()es una operación que abre el flujo. El Javadoc simplemente dice que " Returns a ServletOutputStream"; de manera similar para getWriter(). De cualquier manera, lo que está claro es que HttpServletResponse"es dueño" de la secuencia / escritor, y él (o el contenedor) es responsable de cerrarlo nuevamente.

Entonces, para responder a su pregunta, no, no debe cerrar la transmisión en este caso. El contenedor debe hacer eso, y si ingresa antes, corre el riesgo de introducir errores sutiles en su aplicación.

skaffman
fuente
Estoy de acuerdo con esta respuesta, es posible que también desee consultar ServletResponse.flushBuffer () consulte: docs.oracle.com/javaee/1.4/api/javax/servlet/…
cyber-monk
14
"si abriste el arroyo, entonces deberías cerrarlo. Si no lo hiciste, no deberías" --- bien dicho
Srikanth Reddy Lingala
2
Se siente como las frases publicadas en el tablero escolar "si lo abres, ciérralo. Si lo enciendes, apágalo. Si lo desbloqueas, ciérralo. [...]"
Ricardo
Iv noté que si uso la secuencia al principio de mi código y nunca más, el cliente espera a que se ejecute todo el servlet, pero cuando llamo close()cuando terminé con la secuencia, el cliente regresa inmediatamente y el resto del servlet continúa ejecutándose. ¿No hace eso que la respuesta sea un poco más relativa? en lugar de un sí o no definitivo
BiGGZ
"Si abriste el flujo, deberías cerrarlo. Si no lo hiciste, no deberías. Asegúrate de que el código sea simétrico". - Entonces, ¿qué pasa si luego creas otra transmisión que envuelva esta transmisión? Es difícil mantenerse simétrico en este caso, ya que llamar a cerrar el flujo externo cerrará normalmente el anidado.
steinybot
5

Si existe alguna posibilidad de que se pueda llamar al filtro en un recurso 'incluido', definitivamente no debe cerrar la transmisión. Esto hará que el recurso incluido falle con una excepción de "secuencia cerrada".

Saltar cabeza
fuente
Gracias por agregar este comentario. Curiosamente, todavía estoy lidiando un poco con el problema; recién ahora noté que la plantilla de servlet de NetBeans incluye código para cerrar el flujo de salida ...
Steven Huwig
4

Debe cerrar la secuencia, el código es más limpio ya que invoca getOutputStream () y la secuencia no se le pasa como parámetro, cuando normalmente solo lo usa y no intenta cerrarlo. La API de Servlet no establece que si el flujo de salida puede cerrarse o no debe cerrarse, en este caso puede cerrar el flujo de manera segura, cualquier contenedor se encarga de cerrar el flujo si el servlet no lo cerró.

Aquí está el método close () en Jetty, cierran la secuencia si no se cierra.

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

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

        super.close();
    }

Además, como desarrollador de un filtro, no debe suponer que OutputStream no está cerrado, siempre debe pasar otro OutputStream si desea alterar el contenido después de que el servlet haya hecho su trabajo.

EDITAR: Siempre estoy cerrando la transmisión y no tuve ningún problema con Tomcat / Jetty. No creo que debas tener problemas con ningún contenedor, antiguo o nuevo.

adrian.tarau
fuente
4
"Deberías cerrar el flujo, el código es más limpio ..." - Para mí, el código salpicado con .close () parece menos limpio que el código sin, particularmente si el .close () es innecesario - que es lo que es esta pregunta intentando determinar.
Steven Huwig
1
sí, pero la belleza viene después :) De todos modos, dado que la API no está clara, preferiría cerrarla, el código parece coherente, una vez que solicite un OutputStream, debe cerrarlo a menos que la API diga "no cerrarlo".
adrian.tarau
No creo que cerrar el flujo de salida en mi propio código sea una buena práctica. Ese es el trabajo del contenedor.
JimHawkins
get * no crea la transmisión, no es su productor. No debes cerrarlo, el contenedor debe hacerlo.
dmatej
3

Otro argumento en contra de cerrar el OutputStream. Mira este servlet. Lanza una excepción. La excepción se asigna en web.xml a un JSP de error:

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();
    }
  }
}

El archivo web.xml contiene:

<?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>

Y el 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>

Cuando carga /Erroneousen el navegador, ve la página de error que muestra "Un error". Pero si anula el comentario de la out.close()línea en el servlet anterior, vuelve a implementar la aplicación y vuelve /Erroneousa cargar , no verá nada en el navegador. No tengo ni idea de lo que está pasando realmente, pero supongo queout.close() evita el manejo de errores.

Probado con Tomcat 7.0.50, Java EE 6 usando Netbeans 7.4.

usuario1872904
fuente