Haruskah seseorang memanggil .close () di HttpServletResponse.getOutputStream () /. GetWriter ()?

96

Di Java Servlets, seseorang dapat mengakses badan respon melalui response.getOutputStream()atau response.getWriter(). Haruskah seseorang memanggil .close()ini OutputStreamsetelah itu ditulis?

Di satu sisi, ada nasihat Blochian untuk selalu menutup OutputStreams. Di sisi lain, saya tidak berpikir bahwa dalam kasus ini ada sumber daya yang mendasarinya yang perlu ditutup. Buka / tutup soket dikelola di tingkat HTTP, untuk memungkinkan hal-hal seperti koneksi persisten dan semacamnya.

Steven Huwig
sumber
Anda tidak diundang untuk menebak apakah ada sumber daya yang mendasari untuk ditutup. Jika pelaksana berpikir demikian, atau lebih tepatnya mengetahuinya, dia akan memberikan a close()yang tidak melakukan apa-apa. Yang harus Anda lakukan adalah menutup setiap sumber daya yang dapat ditutup.
Marquis dari Lorne
1
Bahkan jika kode Anda tidak membukanya? Kurasa tidak ...
Steven Huwig

Jawaban:

93

Biasanya Anda tidak harus menutup aliran. Kontainer servlet akan secara otomatis menutup aliran setelah servlet selesai dijalankan sebagai bagian dari siklus hidup permintaan servlet.

Misalnya, jika Anda menutup aliran, streaming tidak akan tersedia jika Anda menerapkan Filter .

Karena itu, jika Anda menutupnya, tidak ada hal buruk yang akan terjadi selama Anda tidak mencoba menggunakannya lagi.

EDIT: tautan filter lain

EDIT2: adrian.tarau benar bahwa jika Anda ingin mengubah respon setelah servlet melakukan tugasnya, Anda harus membuat pembungkus yang memperluas HttpServletResponseWrapper dan menyangga keluaran. Ini untuk menjaga output agar tidak langsung ke klien tetapi juga memungkinkan Anda untuk melindungi jika servlet menutup aliran, sesuai kutipan ini (penekanan saya):

Filter yang mengubah respons biasanya harus menangkap respons sebelum dikembalikan ke klien. Cara untuk melakukan ini adalah dengan mengirimkan servlet yang menghasilkan respons aliran stand-in. Aliran stand-in mencegah servlet menutup aliran respons asli ketika selesai dan memungkinkan filter untuk mengubah respons servlet.

Artikel

Seseorang dapat menyimpulkan dari artikel resmi Sun bahwa menutup OutputStreamdari servlet adalah sesuatu yang biasa terjadi, tetapi tidak wajib.

Nemi
sumber
2
Ini benar. Satu hal yang perlu diperhatikan adalah bahwa dalam beberapa kasus Anda mungkin perlu menyiram aliran, dan itu diperbolehkan.
toluju
1
Ada efek samping lain dari penutupan penulis. Anda juga tidak akan dapat mengatur kode status melalui response.setStatus setelah ditutup.
che javara
1
Ikuti saran ini. Ini akan menghemat banyak rasa sakit. Saya juga tidak akan flush () kecuali Anda tahu mengapa Anda melakukannya - Anda harus membiarkan wadah menangani buffering.
Hal50000
76

Aturan umumnya adalah ini: jika Anda membuka aliran, maka Anda harus menutupnya. Jika tidak, sebaiknya tidak. Pastikan kodenya simetris.

Dalam kasus HttpServletResponse, ini sedikit kurang jelas, karena tidak jelas apakah panggilan getOutputStream()adalah operasi yang membuka aliran. Javadoc hanya mengatakan bahwa itu " Returns a ServletOutputStream"; sama untuk getWriter(). Apa pun itu, yang jelas adalah bahwa HttpServletResponse"memiliki" aliran / penulis, dan itu (atau wadah) bertanggung jawab untuk menutupnya lagi.

Jadi untuk menjawab pertanyaan Anda - tidak, Anda tidak boleh menutup aliran dalam kasus ini. Penampung harus melakukan itu, dan jika Anda masuk ke sana sebelum itu, Anda berisiko memasukkan bug halus dalam aplikasi Anda.

skaffman
sumber
Saya setuju dengan jawaban ini, Anda mungkin juga ingin memeriksa ServletResponse.flushBuffer () lihat: docs.oracle.com/javaee/1.4/api/javax/servlet/…
cyber-monk
14
"jika Anda membuka aliran, maka Anda harus menutupnya. Jika tidak, Anda tidak boleh" --- kata baik
Srikanth Reddy Lingala
2
Rasanya seperti kalimat yang diposting di papan sekolah "jika Anda membukanya, tutup. Jika Anda menyalakannya, matikan. Jika Anda membukanya, kunci. [...]"
Ricardo
IV memperhatikan bahwa saya menggunakan aliran di awal kode saya dan tidak pernah lagi, klien menunggu seluruh servlet untuk dieksekusi, tetapi ketika saya memanggil close()ketika saya selesai dengan aliran, klien segera kembali dan sisa servlet terus mengeksekusi. Bukankah itu membuat jawaban menjadi lebih relatif? sebagai lawan dari ya atau tidak yang pasti
BiGGZ
"jika Anda membuka aliran, maka Anda harus menutupnya. Jika tidak, Anda tidak boleh. Pastikan kodenya simetris." - Jadi bagaimana jika Anda kemudian membuat aliran lain yang membungkus aliran ini? Sulit untuk tetap simetris dalam kasus ini karena memanggil tutup aliran luar biasanya akan menutup aliran yang bersarang.
steinybot
5

Jika ada kemungkinan filter dipanggil pada sumber daya yang 'disertakan', Anda sebaiknya tidak menutup aliran. Ini akan menyebabkan sumber daya yang disertakan gagal dengan pengecualian 'aliran ditutup'.

Lewati Kepala
sumber
Terima kasih telah menambahkan komentar ini. Cukup meriah saya masih bergulat dengan masalah sedikit - baru saja saya perhatikan template servlet NetBeans memang menyertakan kode untuk menutup aliran keluaran ...
Steven Huwig
4

Anda harus menutup aliran, kodenya lebih bersih karena Anda memanggil getOutputStream () dan aliran tidak diteruskan kepada Anda sebagai parameter, ketika biasanya Anda hanya menggunakannya dan tidak berusaha menutupnya. API Servlet tidak menyatakan bahwa jika aliran keluaran dapat ditutup atau tidak boleh ditutup, dalam hal ini Anda dapat menutup aliran dengan aman, wadah apa pun di luar sana akan menangani penutupan aliran jika tidak ditutup oleh servlet.

Berikut adalah metode close () di Jetty, mereka menutup aliran jika tidak ditutup.

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

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

        super.close();
    }

Juga sebagai pengembang Filter Anda tidak boleh menganggap bahwa OutputStream tidak ditutup, Anda harus selalu meneruskan OutputStream lain jika Anda ingin mengubah konten setelah servlet melakukan tugasnya.

EDIT: Saya selalu menutup streaming dan saya tidak punya masalah dengan Tomcat / Jetty. Saya tidak berpikir Anda harus memiliki masalah dengan wadah apa pun, lama atau baru.

adrian.tarau
sumber
4
"Anda harus menutup aliran, kodenya lebih bersih ..." - Bagi saya, kode yang dibumbui dengan .close () terlihat kurang bersih daripada kode tanpa, terutama jika .close () tidak diperlukan - itulah pertanyaan ini mencoba untuk menentukan.
Steven Huwig
1
ya, tapi kecantikan datang setelahnya :) Bagaimanapun, karena API tidak jelas Saya lebih suka menutupnya, kode terlihat konsisten, setelah Anda meminta OutputStream Anda harus menutupnya kecuali jika API mengatakan "jangan tutup".
adrian.tarau
Saya tidak berpikir menutup aliran keluaran dalam kode saya sendiri adalah praktik yang baik. Itulah tugas wadah.
JimHawkins
get * tidak membuat streaming, bukan produsernya. Anda tidak boleh menutupnya, wadah harus melakukannya.
dmatej
3

Argumen lain yang menentang penutupan OutputStream. Lihat servlet ini. Ini membuat pengecualian. Pengecualian dipetakan di web.xml ke kesalahan 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();
    }
  }
}

File web.xml berisi:

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

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

Saat Anda memuat /Erroneousdi browser, Anda melihat halaman kesalahan yang menampilkan "Kesalahan". Tetapi jika Anda menghapus komentar out.close()baris di servlet di atas, menerapkan ulang aplikasi, dan memuat ulang /ErroneousAnda tidak akan melihat apa-apa di browser. Saya tidak tahu apa yang sebenarnya terjadi, tapi saya rasa itu out.close()mencegah penanganan kesalahan.

Diuji dengan Tomcat 7.0.50, Java EE 6 menggunakan Netbeans 7.4.

pengguna1872904
sumber