Doit-on appeler .close () sur HttpServletResponse.getOutputStream () /. GetWriter ()?

96

Dans les servlets Java, on peut accéder au corps de la réponse via response.getOutputStream()ou response.getWriter(). Doit-on appeler .close()cela OutputStreamaprès avoir été écrit?

D'une part, il y a l'exhortation blochienne de toujours fermer l' OutputStreamart. D'un autre côté, je ne pense pas que dans ce cas, il y a une ressource sous-jacente qui doit être fermée. L'ouverture / la fermeture des sockets est gérée au niveau HTTP, pour permettre des choses comme les connexions persistantes et autres.

Steven Huwig
la source
Vous n'êtes pas invité à deviner s'il existe une ressource sous-jacente à fermer. Si le implementor le pense, ou plutôt sait si, il fournira un close()qui ne fait rien. Ce que vous devez faire est de fermer toutes les ressources fermables.
Marquis de Lorne
1
Même si votre code ne l'a pas ouvert? Je ne pense pas ...
Steven Huwig

Réponses:

93

Normalement, vous ne devez pas fermer le flux. Le conteneur de servlet fermera automatiquement le flux une fois le servlet exécuté dans le cadre du cycle de vie de la demande de servlet.

Par exemple, si vous fermiez le flux, il ne serait pas disponible si vous implémentiez un filtre .

Cela dit, si vous le fermez, rien de grave ne se passera tant que vous n'essayez pas de l'utiliser à nouveau.

EDIT: un autre lien de filtre

EDIT2: adrian.tarau est correct en ce sens que si vous voulez modifier la réponse après que le servlet a fait son travail, vous devez créer un wrapper étendant HttpServletResponseWrapper et tamponner la sortie. C'est pour empêcher la sortie d'aller directement au client mais vous permet également de vous protéger si le servlet ferme le flux, comme dans cet extrait (c'est moi qui souligne):

Un filtre qui modifie une réponse doit généralement capturer la réponse avant qu'elle ne soit renvoyée au client. La manière de procéder consiste à transmettre au servlet qui génère la réponse un flux de secours. Le flux de secours empêche le servlet de fermer le flux de réponse d'origine lorsqu'il se termine et permet au filtre de modifier la réponse du servlet.

Article

On peut déduire de cet article officiel de Sun que la fermeture OutputStreamd'un servlet est quelque chose qui est une occurrence normale, mais n'est pas obligatoire.

Nemi
la source
2
C'est correct. Une chose à noter est que dans certains cas, vous devrez peut-être vider le flux, ce qui est parfaitement autorisé.
toluju
1
Il y a un autre effet secondaire de la fermeture de l'écrivain. Vous ne pourrez pas non plus définir le code d'état via response.setStatus une fois qu'il a été fermé.
che javara
1
Suivez ces conseils. Cela vous évitera beaucoup de douleur. Je ne viderais pas () non plus à moins que vous ne sachiez pourquoi vous le faites - vous devriez laisser le conteneur gérer la mise en tampon.
Hal50000
75

La règle générale d'entre eux est la suivante: si vous avez ouvert le flux, vous devez le fermer. Sinon, vous ne devriez pas. Assurez-vous que le code est symétrique.

Dans le cas de HttpServletResponse, c'est un peu moins clair, car il n'est pas évident si l'appel getOutputStream()est une opération qui ouvre le flux. Le Javadoc dit simplement que " Returns a ServletOutputStream"; de même pour getWriter(). Dans tous les cas, ce qui est clair, c'est que HttpServletResponse"possède" le flux / l'écrivain, et qu'il (ou le conteneur) est responsable de le refermer.

Donc, pour répondre à votre question - non, vous ne devez pas fermer le flux dans ce cas. Le conteneur doit le faire, et si vous y entrez avant, vous risquez d'introduire de subtils bogues dans votre application.

skaffman
la source
Je suis d'accord avec cette réponse, vous pouvez également consulter le ServletResponse.flushBuffer () voir: docs.oracle.com/javaee/1.4/api/javax/servlet/…
cyber-monk
14
"si vous avez ouvert le flux, vous devriez le fermer. Sinon, vous ne devriez pas" --- bien dit
Srikanth Reddy Lingala
2
Cela ressemble aux phrases affichées sur le conseil scolaire "si vous l'ouvrez, fermez-le. Si vous l'activez, éteignez-le. Si vous le déverrouillez, verrouillez-le. [...]"
Ricardo
Iv a remarqué que si j'utilise le flux au début de mon code et plus jamais, le client attend que tout le servlet s'exécute, mais lorsque j'appelle close()quand j'ai terminé avec le flux, le client revient immédiatement et le reste du servlet continue de s'exécuter. Cela ne rend-il pas la réponse un peu plus relative? par opposition à un oui ou un non définitif
BiGGZ
"si vous avez ouvert le flux, vous devriez le fermer. Sinon, vous ne devriez pas. Assurez-vous que le code est symétrique." - Et si vous créez ensuite un autre flux qui enveloppe ce flux? Difficile de rester symétrique dans ce cas, car l'appel de close out le flux externe fermera généralement le flux imbriqué.
steinybot
5

S'il y a une chance que le filtre soit appelé sur une ressource «incluse», vous ne devriez certainement pas fermer le flux. Cela entraînera l'échec de la ressource d'inclusion avec une exception «flux fermé».

Passer la tête
la source
Merci d'avoir ajouté ce commentaire. De manière assez hilarante, je suis encore un peu aux prises avec le problème - tout à l'heure, j'ai remarqué que le modèle de servlet de NetBeans inclut du code pour fermer le flux de sortie ...
Steven Huwig
4

Vous devez fermer le flux, le code est plus propre puisque vous appelez getOutputStream () et le flux ne vous est pas passé en tant que paramètre, alors que vous l'utilisez généralement et n'essayez pas de le fermer. L'API Servlet n'indique pas que si le flux de sortie peut être fermé ou ne doit pas être fermé, dans ce cas, vous pouvez fermer le flux en toute sécurité, tout conteneur là-bas s'occupe de fermer le flux s'il n'a pas été fermé par le servlet.

Voici la méthode close () dans Jetty, ils ferment le flux s'il n'est pas fermé.

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

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

        super.close();
    }

De même, en tant que développeur d'un filtre, vous ne devez pas présumer que OutputStream n'est pas fermé, vous devez toujours transmettre un autre OutputStream si vous souhaitez modifier le contenu une fois que le servlet a fait son travail.

EDIT: Je ferme toujours le flux et je n'ai eu aucun problème avec Tomcat / Jetty. Je ne pense pas que vous devriez avoir des problèmes avec un conteneur, ancien ou nouveau.

adrian.tarau
la source
4
"Vous devriez fermer le flux, le code est plus propre ..." - Pour moi, le code parsemé de .close () semble moins propre que le code sans, en particulier si le .close () est inutile - c'est ce que cette question est essayer de déterminer.
Steven Huwig
1
oui, mais la beauté vient après :) Quoi qu'il en soit, puisque l'API n'est pas claire, je préférerais la fermer, le code semble cohérent, une fois que vous demandez un OutputStream, vous devez le fermer à moins que l'API ne dise "ne le fermez pas".
adrian.tarau
Je ne pense pas que la fermeture du flux de sortie dans mon propre code soit une bonne pratique. C'est le travail du conteneur.
JimHawkins
get * ne crée pas le flux, ce n'est pas son producteur. Vous ne devez pas le fermer, le conteneur doit le faire.
dmatej
3

Un autre argument contre la fermeture du OutputStream. Regardez ce servlet. Cela lève une exception. L'exception est mappée dans le fichier web.xml à une erreur 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();
    }
  }
}

Le fichier web.xml contient:

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

Et l'erreur.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>

Lorsque vous chargez /Erroneousdans le navigateur, vous voyez la page d'erreur affichant "Une erreur". Mais si vous annulez le commentaire de la out.close()ligne dans le servlet ci-dessus, redéployez l'application et rechargez, /Erroneousvous ne verrez rien dans le navigateur. Je n'ai aucune idée de ce qui se passe réellement, mais je suppose que cela out.close()empêche la gestion des erreurs.

Testé avec Tomcat 7.0.50, Java EE 6 avec Netbeans 7.4.

user1872904
la source