Closing input streams? [duplicate] - java

In Java Servlets, one can access the response body via response.getOutputStream() or response.getWriter(). Should one call .close() on this OutputStream after it has been written to?
On the one hand, there is the Blochian exhortation to always close OutputStreams. On the other hand, I don't think that in this case there is an underlying resource that needs to be closed. The opening/closing of sockets is managed at the HTTP level, to allow things like persistent connections and such.

Normally you should not close the stream. The servlet container will automatically close the stream after the servlet is finished running as part of the servlet request life-cycle.
For instance, if you closed the stream it would not be available if you implemented a Filter.
Having said all that, if you do close it nothing bad will happen as long as you don't try to use it again.
EDIT: another filter link
EDIT2: adrian.tarau is correct in that if you want to alter the response after the servlet has done its thing you should create a wrapper extending HttpServletResponseWrapper and buffer the output. This is to keep the output from going directly to the client but also allows you to protect if the servlet closes the stream, as per this excerpt (emphasis mine):
A filter that modifies a response must
usually capture the response before it
is returned to the client. The way to
do this is to pass the servlet that
generates the response a stand-in
stream. The stand-in stream prevents
the servlet from closing the original
response stream when it completes and
allows the filter to modify the
servlet's response.
Article
One can infer from that official Sun article that closing the OutputStream from a servlet is something that is a normal occurrence, but is not mandatory.

The general rule of them is this: if you opened the stream, then you should close it. If you didn't, you shouldn't. Make sure the code is symmetric.
In the case of HttpServletResponse, it's a bit less clear cut, since it's not obvious if calling getOutputStream() is an operation that opens the stream. The Javadoc just says that it "Returns a ServletOutputStream"; similarly for getWriter(). Either way, what is clear is that HttpServletResponse "owns" the stream/writer, and it (or the container) is responsible for closing it again.
So to answer your question - no, you should not close the stream in this case. The container must do that, and if you get in there before it, you risk introducing subtle bugs in your application.

If there is any chance the filter might be called on an 'included' resource, you should definitely not close the stream. This will cause the including resource to fail with a 'stream closed' exception.

You should close the stream, the code is cleaner since you invoke getOutputStream() and the stream is not passed to you as a parameter, when usually you just use it and don't attempt to close it. The Servlet API doesn't states that if the output stream can be closed or must not be closed, in this case you can safely close the stream, any container out there takes care of closing the stream if it was not closed by the servlet.
Here is the close() method in Jetty, they close the stream if it not closed.
public void close() throws IOException
{
if (_closed)
return;
if (!isIncluding() && !_generator.isCommitted())
commitResponse(HttpGenerator.LAST);
else
flushResponse();
super.close();
}
Also as a developer of a Filter you should not presume that the OutputStream is not closed, you should always pass another OutputStream if you want to alter the content after the servlet has done its job.
EDIT : I'm always closing the stream and I didn't had any problems with Tomcat/Jetty. I don't think you should have any problems with any container, old or new.

Another argument against closing the OutputStream. Look at this servlet. It throws an exception. The exception is mapped in the web.xml to an error 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();
}
}
}
The web.xml file contains:
<?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>
And the 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>
When you load /Erroneous in the browser you see the error page displaying "An error".
But if you un-comment the out.close() line in the above servlet, redeploy de application, and reload /Erroneous you’ll see nothing in the browser. I have no clue about what is actually happening, but I guess that out.close() prevents the error handling.
Tested with Tomcat 7.0.50, Java EE 6 using Netbeans 7.4.

If you're using Spring with Spring Security, you should not close the stream or writer.
Stream returned from ServletResponse.getOutputStream() or writer returned from ServletResponse.getWriter() will commit the response upon closing. Committing the response, as explained here, means that http status and headers become immutable and Spring framework will not be able to adjust http status even if exception gets thrown during serving this request.
An instance of OnCommittedResponseWrapper class is used as implementation of ServletResponse and here's the code responsible for this behavior (check javadoc as well).
Consider the following example controller:
#RestController
public class MyController {
#RequestMapping(method = RequestMethod.POST, value = "/blah")
public void entrypoint(ServletRequest request, ServletResponse response) throws IOException {
try (var writer = response.getWriter()) {
throw new RuntimeException("Something bad happened here");
}
}
When the exception is thrown, the first thing that happens is a call to writer.close() that'll freeze response http status to its default value 200.
Only after that the exception will start propagating from this controller to Spring error handlers. Spring error handlers will not be able to change the status to 500 because the response was committed already and so the status will remain 200.

Related

OncePerRequestFilter unable to set error code different than 404

I have a Spring application (not using Spring boot) deployed to tomcat
I'm trying to return error 401 (HttpServletResponse.SC_UNAUTHORIZED) on specific URLs in given condition using OncePerRequestFilter ,
But I keep getting Not found error:
Response code: 404
My Filter(removed conditions):
#Component
public class MyFilter extends OncePerRequestFilter {
private static final Logger logger = Logger.getLogger(MyFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Not autorized");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
PrintWriter out = response.getWriter();
out.write(""); // Empty
out.flush();
} catch (IOException e) {
logger.error("error filtering", e);
}
return; // stop chain
}
}
I tried using similar code to previous similar answer
I believe you can response.sendError inside do Filter method.
EDIT
If I just throw an exception instead I get a generic error code 500
Response code: 500
EDIT 2
I'm adding filter inside onStartup method overriden WebApplicationInitializer's
FilterRegistration myFilter = servletContext.addFilter("myFilter ", MyFilter.class);
myFilter.addMappingForUrlPatterns(null, false, "/myservlet/myendpoint/*");
EDIT 3
Also my filter in #Componenet and its package include in component scan
#ComponentScan(basePackages = { "my.parent.pacakge"})
I tried your code and it works. Different error codes were getting successfully returned. Although, I wanna point out a couple of things-
The method sendError() sends the response to the client and also clears the response buffer. So, anything you write after it into the response after that is of no use.
Calling setStatus() is of no use either. The HTTP response and HTTP response code has already been sent to the client in the above line when you called sendError().
Now, this is my hypothesis why your code is not working for you, but it is for me-
The reason that you might be getting HTTP 404 is that your API is not present. This can be due to a spelling error or maybe due to a simple ignorance like calling your API named /foo/bar like /foo/bar/ i.e. with extra trailing forward slash. I believe that your filter is getting executed successfully and you must be doing something useful there, not the sample code of sendError() that you have explained in the question.
Also, when you throw an Exception in your filter, the control does not reach to the API and API lookup does not happen. Hence, the default HTTP 500 response is sent to the client due to the unhandled exception instead of HTTP 404.
It return 401 as expected only if setStatus is executed after out.write
PrintWriter out = response.getWriter();
out.write("");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

How to avoid sending HTTP request from a Java web server to itself?

Real situation is like this: Java web server (Weblogic) recieves a request from user for which it has to send a ZIP archive in response. Archive has to be dynamically generated from some files user asked for and one HTML report generated by the server itself. I would like to reuse JSF servlets the server already uses in other cases to generate this report. So, basically, what I use is:
HttpURLConnection self = new URL ("http://me.myself.com/report.jsf?...").openConnection ();
String report_html = fetchHtmlFromConnection (self);
and then create the requested ZIP, including the generated HTML in it.
The question is, can I somehow avoid making an internal HTTP request (to report.jsf) in this scenario? That involves basically pointless (since application just "talks" to itself anyway) roundtrips through operating system, HTTPD (which might be on a different machine), etc.
I am not very familiar with JSF, but from what I understand of them you can use a technic that is also applicable to JSP pages:
Create your own HttpServletResponseWrapper (a class used by the container that lets you modify the response)
Use it to override the default Writer (that writes the rendered page to the output) and provide one that writes the output to a String or a temporary file that will feed the compressing code.
There is a nice and simple tutorial that shows you how to do that:
http://blog.valotas.com/2011/09/get-output-of-jsp-or-servlet-response.html
Then
As hinted by gyan, get a ServletRequestDispatcher from your servlet that will let you invoke the rendering of the JSF
Forward the servlet call in order to provide your own HttpServletResponseWrapper
Use your HttpServletResponseWrapper to get the rendered HTML and feed it to the zipping code.
So the zipping Servlet would be like:
TempFileRespWrapper respWrapper = new TempFileRespWrapper();
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( "/report.jsf");
dispatcher.forward(request, respWrapper);
File f = respWrapper.getOutputPath();
addFileToZip(f);
You should have a business service layer so that a "generateReport" service could be used inside multiple presentation views (even the "zip file" one).
You could do this in standard J2EE way via EJBs or through any custom framework that let you specify injectable business services (e.g. spring).
I think the main problem here is that you can generate the report only through the client interface (http). That makes it an isolated service, not available to other parts of the application. Basically you need a refactoring.
Do not code business inside JSFs or similar. (by the way, try not using jsf at all :D )
BUSINESS LAYER PRESENTATION
generateReportService---|---jsf-HtmlReport
\__|___
| \
someOtherContentService-|----jsf-Zip
Think about request dispatcher strategy, where request/response object would be sent to the report servlet from the entry servlet. In turn report servlet would generate the report and control can be sent to next servlet, which completes the rest of zip and send process.
For constructing a RequestDispatcher object, you can use either the ServletRequest.getRequestDispatcher() method or the ServletContext.getRequestDispatcher() method.
RequestDispatcher dispatcher=getServletContext().getRequestDispatcher( "/report.jsf" );
dispatcher.forward( request, response );
The next servlet set mime type as 'application/zip' and write the zip binary to browser. The user's browser would handle the content in the form of download depending on the browser settings.
Make sure that your web server is configured to cache (generated) html and you will not have to worry about it. First request to full or partial URL will generate a call to jsf generator and later it will be fetched from web server cache.
Yes, there will be a little overhead involved
(your source -> we server -> generator page or cache)
But it is going to be easier in the end.
Tested Solution!
This solution actually get ideas already posted here (specially from #gyan).
Write a Servlet to zip
(you could use an filter for that too. For example, suppose that you have a ZipFilter. You could map the filter for all *.zip and chain that filter with a URLRewrite filter for respective .jsf URL).
public class ZipServlet
extends HttpServlet {
#Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException,
IOException {
ZipEntry zipEntry = new ZipEntry("helloWorld.html");
ZipHttpServletResponseWrapper respWrapper = new ZipHttpServletResponseWrapper(response, zipEntry);
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/helloWorld.jsf");
dispatcher.forward(request, respWrapper);
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "inline; filename=output.zip;");
response.flushBuffer();
respWrapper.getOutputStream().close();
}
}
NOTE : Yes, you should use RequestDispatcher
ZipHttpServletResponseWrapper
There's no much to say about that. From Java 7, you can use a native Class to create zip files properly. Using Decorator pattern with ZipOutputStream on top of response.getOutputStream() should be recommended way.
Remember that HttpServletResponseWrapper is a decorator. You should not use that if you don't want to reuse the target "servlet" output directly (you could use an stunt HttpServletResponse rather than use HttpServletResponseWrapper).
public class ZipHttpServletResponseWrapper
extends HttpServletResponseWrapper {
private ZipEntry entry;
private ZipServletOutputStreamWrapper streamWrapper;
private ZipOutputStream outputStream;
private PrintWriter printWriter;
public ZipHttpServletResponseWrapper(HttpServletResponse response, ZipEntry entry) {
super(response);
this.entry = entry;
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
if (streamWrapper == null) {
outputStream = new ZipOutputStream(this.getResponse().getOutputStream());
outputStream.putNextEntry(entry);
streamWrapper = new ZipServletOutputStreamWrapper(outputStream);
}
return streamWrapper;
}
#Override
public PrintWriter getWriter() throws IOException {
if (printWriter == null) {
printWriter = new PrintWriter(getOutputStream());
}
return printWriter;
}
private class ZipServletOutputStreamWrapper
extends ServletOutputStream {
private ZipOutputStream outputStream;
public ZipServletOutputStreamWrapper(ZipOutputStream outputStream) {
this.outputStream = outputStream;
}
#Override
public void close() throws IOException {
outputStream.closeEntry();
outputStream.finish();
}
#Override
public void write(int b) throws IOException {
outputStream.write(b);
}
}
}
Now, the secret: mapping wisely!
Some important parts of JSF could use Filter chain (for example, myfaces from Apache use some extensions to provide some JSF components). In this case, you should specify in these filters digest FORWARD and REQUEST
<filter-mapping>
<filter-name>extensionsFilter</filter-name>
<url-pattern>*.jsf</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

Global exception page not selected by Spring MVC

Continuing the thread: Global exception page in Apache Tiles and Spring MVC
I have an error page defined in my web.xml:
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/404.jsp</location>
</error-page>
I have noticed one more issue in Spring MVC:
a)
if no #RequestMapping is matched then indeed, my custom error jsp is printed.
b)
if a #RequestMapping is matched, but the method sets an error status eg.
response.setStatus(404);
then Tomcat's (7.0.29) default error page is chosen, not my jsp.
Why? How to make my 404 page be displayed always ?
I think what you're experiencing is caused by the line you mentioned: response.setStatus(404);
This method doesn't trigger the container's error page mechanism, it should be used when there is no error. To trigger the mechanism, you have to use sendError, which is recommended in the official docs.
BTW I've just found out that the behavior differs between Servlet Spec. 2.3 and 2.4
(read here). In 2.3 the two methods are said to do the very same thing, whereas in 2.4 they differ..............
With spring MVC is preferable using build-in exception handler to show error page to the users.
Take a look this tutorial: http://doanduyhai.wordpress.com/2012/05/06/spring-mvc-part-v-exception-handling/
You may want to take a look at ExceptionHandler.
It is really nice and flexible and allows you to implement logic to display different error pages and output different HTTP reponse codes depending on the exception (this is not always a requirement but is nice to know you could do that easily).
I paste here my code as I think it can be useful to solve common issues regarding this topic.
#ExceptionHandler(Exception.class)
public ModelAndView resolveException(Exception ex,
HttpServletRequest request,
HttpServletResponse response) {
// I get an email if something goes wrong so that I can react.
if (enableEmailErrorReporting)
sendExceptionEmail(request.getRequestURL().toString(), ex);
ModelAndView mav = getModelAndView(ex, request);
setStatusCode(ex, response);
return mav;
}
protected ModelAndView getModelAndView(Exception ex,
HttpServletRequest request) {
// Here you can implement custom logic to retrieve the correct
// error page depending on the exception. You should extract
// error page paths as properties or costants.
return new ModelAndView("/WEB-INF/app/error.html");
}
// This is really nice.
// Exceptions can have status codes with the [`ResponseStatus`][2] annotation.
private void setStatusCode(Exception ex, HttpServletResponse response) {
HttpStatus statusCode = HttpStatus.BAD_REQUEST;
ResponseStatus responseStatus =
AnnotationUtils.findAnnotation(ex.getClass(),
ResponseStatus.class);
if (responseStatus != null)
statusCode = responseStatus.value();
response.setStatus(statusCode.value());
}
The logic here is that a controller method throws an uncatched exception. Spring will invoke the method marked with ExceptionHandler (you can have one per controller, per exception, or a global default one, usually I make all my controllers inherit from a BaseController class where I define this method). Passed to the method are the exception itself and any other info you need to choose the right view to display. Even more, you can see if on the exception has been declared a specific HTTP response code (eg, 500 for unchecked exception, 400 for validation errors, etc...) and return that code along with your error page.

How To Stream Text Response From a Java Spring MVC 3.0 Webapp

How can I stream text output to the page on the browser to show the progress of an operation that may take about 15 - 20 seconds? I've tried writing to the output stream of HttpServletResponse directly, but the user still sees the full output after the whole process is finished.
This is what I've tried so far
#RequestMapping(value = "/test")
public void test(HttpServletResponse response)
throws IOException, InterruptedException {
response.getOutputStream().println("Hello");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("How");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("are");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("you");
response.getOutputStream().flush();
}
I am no Spring MVC expert, but I would think you would do something like send a 202 response code of "accepted", which indicates the server has received the request and is going to do some asynchronous processing. Usually, the server provides a URL to allow the client to issue requests about the status of the operation. What you are trying to do violates the usual way server/client relationships work. The client calls the server, and the server responds and then the connection is closed. In what context are you trying to do this and for what reason? Perhaps I could offer some more insight or think of another way to do it?
try to use:
response.flushBuffer();
as JavaDoc says:
Forces any content in the buffer to be written to the client. A call to this method automatically commits the response, meaning the status code and headers will be written.
This worked for me when testing with Chrome
response.setBufferSize(0);
response.setContentType("text/event-stream");
... write content ...
#Controller
public class MyController{
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String getTest() {
return "hello how are you";
}
}
If you are using spring controller you could do the above with the response body annotation.

How do I specify a query string in Tomcat's <servlet-mapping> <url-pattern>?

I am running Tomcat 5.5.4 and have a servlet running with no problems. However, I'd like to set up a mapping to only launch the servlet when a URL containing a particular query string is submitted.
Right now in web.xml I have:
<servlet-mapping>
<servlet-name>MyServer</servlet-name>
<url-pattern>/go/*</url-pattern>
</servlet-mapping>
If a browser submits http://localhost/MyServer/go?P=123 the servlet is launched and all is well. However, I'd like to only launch that servlet if the URL is exactly as just shown. Unfortunately, right now if the URL is http://localhost/MyServer/go?P=AnyDarnThing the servlet still launches. I have tried setting up the following:
<url-pattern>/go?P=123</url-pattern>
but this results in The requested resource (/MyServer/go) is not available.
I've tried numerous variations (quoting the string, ...) on the above URL pattern but I always get the above error. I notice that if I (for debugging purposes) drop the "?" as in
<url-pattern>/goP=123</url-pattern>
I no longer get the error message and the server launches (but, of course, it doesn't respond to the "query string" because it's not properly formed.) This suggest to me that the "?" is causing a problem in the mapping. I've tried replacing it with its URL special character equivalent as follows:
<url-pattern>/go%3FP=123</url-pattern>
but this gives the same result just described above when I tried dropping the "?" altogether.
I realize I can let the servlet get launched when any query string is submitted and then "ignore" the request for all but the one I care about but there is a reason I'd prefer to not have the servlet launched to begin with. So, my question is, how can I configure the servlet so that it is only launched when a specific query string is included?
Thank you.
You can't do that. The url-pattern is pretty limited.
If you want to have distinct actions taken based on a GET parameter, you can do that manually. In the doGet() method of the servlet have a simple if-clause and invoke different methods depending on the query string / get param.
You can't do that using URL patterns.
You can achive this using filters. Implement a filter which will forward to the Servlet only if the query params exists.
Here is the how the filter will look like:
public class ServletAcessFilter implements Filter
{
public void init(FilterConfig filterConfig) throws ServletException
{
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException
{
//validate the request, check if the request can be forwarded to servlet.
if(request.getParameter("P").equalsIgnoreCase("123")){
filterChain.doFilter(request, response);
} else {
//write what you want to do if the request has no access
//below code will write 404 not found, you can do based on your requirement
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(404);
}
}
public void destroy()
{
}
}
Define the filter in the web.xml like this:
<filter>
<filter-name>ServletAccessFilter</filter-name>
<filter-class>com.ServletAcessFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ServletAccessFilter</filter-name>
<url-pattern>/go/*</url-pattern>
</filter-mapping>
To add to Bozho response, you may also try to move to Clean URLs
This will greatly increase your options in terms of URL pattern matching, and, in particular, may significantly ease configuration of a fronting reverse proxy if you ever need one.

Categories

Resources