JSP error page appended to previous output - java

I have a Spring MVC project where I am using controller advice to handle errors thrown in controllers. However, I also want to display a nice error page if an error occurs within JSP files (even though this really shouldn't happen!). Therefore I have added the following to my project's web.xml file:
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/application/error/view-error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/views/application/error/view-error.jsp</location>
</error-page>
If I trigger an error in JSTL on purpose, the contents of view-error.jsp is rendered fine. However, the content is appended to the output of the JSP file in which the error occurred. For instance, if an error occurs within display-users.jsp at line 50, the result is that the output that was generated before the error occurred (line 1-50) is prepended the contents in view-error.jsp.
This is very undesirable as it generates a funky looking error page. And since I cannot tell where an exception will be thrown (if I could, I would fix the error), then what the user sees is very likely to look bad.
I guess it's because the output is already in the buffer, and may already have been sent to the client? Is there any way I can fix this, or perhaps an alternative approach? Thanks!

This is a problem with large JSP generating big HTML, with scriptlet java code intermixed everywhere. As soon as enough data have been writen, the server commits the headers (sends them to client) and send the beginning of the page. At that moment, you can no longer rollback anything to get back the data that has already been received (and possibly displayed) by the browser.
That's one of the reasons why scriplet are not recommended, and if you really need to put some intelligence it the JSP, it should be at the beginning of the page before anything is actually sent to browser. But ideally, everything should have been computed in advance in a servlet and prepared data put in request attributes. That way the JSP should only contain simple conditionnal or loop tags in addition to HTML output and request attributes rendition. All that with little risk to generate an exception.

Looks like the OutputStream of the HttpServletResponse is being written to before the enitre JSP finishes rendering.
This ideally should be controllable by "autoflush" property. https://tomcat.apache.org/tomcat-5.5-doc/jspapi/javax/servlet/jsp/JspWriter.html
But just in case it isn't solvable by that:
You could intercept anything that written to HttpServletResponse by using the HttpServletResponseWrapper approach.
The general idea there is that you create a Filter and that Filter will pass a "Response Wrapper" to the layers below. This Response Wrapper holds a reference to real Response instance. Anything that gets written to the Response, can be then manipulated by the Response Wrapper and then sent to the real Response instance.
So, for your case, you could append all the data in a StringBuilder, and when then controls returns back to the Filter, the Filter can print the entire StringBuilder to the real Response's OutputStream.
Here is an example that intercepts anything the Servlets, etc. write and then sends the GZipped version of that to the Browser:
http://tutorials.jenkov.com/java-servlets/gzip-servlet-filter.html

Been there, done that. Here's a quick and dirty workaround until you can redesign.
1) Place the all the JSTL code that generates output in a new JSP -- let's call it display-users-view.jsp (call it whatever you want).
2) Import display-users-view.jsp from your display-users.jsp page via a <c:import>, but make sure you dump the contents to a var(!). e.g.:
<c:import url="display-users-view.jsp" var="output"/>
3) As a final step in display-users.jsp, dump the output to the screen with a simple:
${output}
Now, if the error is thrown before the ${output} .. no harm, no foul because you haven't output anything to the browser yet. If there is no error, the ${output} will dump the HTML that was generated in the display-users-view.jsp.
Note, by using c:import you don't have to pass any querystring or form params that were submitted to display-users.jsp because you will still have them available in your display-users-view.jsp.

Related

How to check if there was the execution of the servlet is Exception free

Hi I am fairly new to Java Servlets. I want to ensure that the execution on the server side code is exception free i.e I want to check if there was any exception thrown on the server code. How can I determine that using the response that I get?
Thank you
Since, a Servlet executes server-side it's the server-side code that determines whether the client should be made aware of an exception or not. A client wouldn't come to know of it automatically unless the exception is not handled (within the servlet code) and no error pages were set for the web application (both of which aren't recommended).
Ideally, a client should never be made aware of the exact exception. Instead, the client should just receive a proper error message as to why the current request couldn't get processed successfully.
Error details should be shared on need to know basis. For example, if the input validation fails it makes sense to include the name of the field that was invalid in your error message. It isn't proper to show a NumberFormatException instead (say, because the client entered an age that was non-numeric).
EDIT:
There's no one single right approach here. There are various methods you can adopt based on how much descriptive your web application wants to be about the error to your client.
If you just require to let the client know that an error occurred with a brief error description you can use sendError() (which behind the scenes would automatically set the HTTP status header to an error code like 400 Bad Request below)
if (request.getParameter("id") == null)
response.sendError(response.SC_BAD_REQUEST, "ID parameter missing");
Other approaches include configuring <error-pages> in your web.xml for different error codes or exceptions. The container would take care of automatically forwarding to the .jsp files configured below.
<error-page>
<error-code>404</error-code>
<location>/error404.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</error-code>
<location>/errorPage.jsp</location>
</error-page>
The JSPs need to have their isErrorPage attribute set to true to receive an implicit exception object to get more information about the exception. For example, exception.getMessage() could be used (in some cases) to generate the error message to be shown to the user.
The client side
The client doesn't need to do anything special (like a getError()) to receive the error. The client would still be receiving HTML that gets rendered by the browser just like if the request had succeeded but since it didn't, the client would receive the error message formatted as HTML (can even view > source it).
The HTML sent depends on whether you used sendError() (which would send the standard error response from your web server like Tomcat) or your own configured <error-page> JSPs in which case you have complete control over your HTML error template.
If you get the content from the servlet, there where not unhandled exceptions. If you want to get info of handled exceptions, log them in their catch clause.
Use log4j to catch your exception (using try catch block) but if this is some kind of homework you are working then this might be one way you could check based on the http return code. you can use jquery http://jquery.com/. Here is sample code for 404 error.
Also this page has all the http error code and what they mean.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
$(function() {
var urlPath = "some_url";
$.ajax(urlPath,
{
statusCode: {
404: function() {
alert('page not found');
}
}
});
});
</script>

Multiple Request Response Forward

I am currently passing some ArrayList's from my servlet to my JSP page using the below code.
RequestDispatcher rd = null;
request.setAttribute("date",al);
request.setAttribute("bau",al1);
request.setAttribute("project",al2);
rd = request.getRequestDispatcher("ResourceChart.jsp");
rd.forward(request,response);
The problem is when I click refresh on the page, the same date is passed again and I end up with odd results. Should I be forwarding to the JSP page as well to avoid the servlet regenerating the variables and passing them?
Whats the best way to implement this?
You're apparently refreshing a POST request and ignoring/confirming the browser's builtin warning that the data would be resent. In that case, the doPost() method will indeed be re-executed with the previously submitted data which would only end up in duplicate entries in the DB.
The canonical approach to avoid the double submit on a refresh of a POST request is to send a redirect after POST request. A redirect implicitly creates a new GET request. Refreshing would then result in only the GET request being re-executed which is totally harmless.
To achieve that, replace all in the question shown lines of the doPost() by the following single line
response.sendRedirect(request.getContextPath() + "/ResourceChart");
(assuming that your servlet is mapped on an URL pattern of /ResourceChart)
and create a new doGet() method wherein you put all those removed lines back, along with the necessary logic to prepare the lists. The only change which you need to make is to put the JSP file in /WEB-INF folder to avoid endusers from being able to open it directly by entering/guessing its URL without calling the servlet's doGet() first.
request.getRequestDispatcher("/WEB-INF/ResourceChart.jsp").forward(request, response);
See also:
Our Servlets wiki page

How do I send a list to the jsp that requested it?

Let us suppose there is a jsp that needs to display the list of files shared shared by a particular IP. Client opens that jsp on his local server and the request is sent to a remote server to fetch the list.
Servlet on the server processes the request and fetches the list of files shared by that IP. Now how do the servlet send that list to the jsp page that requested it ?
JSP :
connection.openConnection(); // Connection sends IP as the query parameter to the
// remote servlet
Servlet :
doGet(..parameters..) {
list = getList(forThatIP);
// NOW HOW DO I SEND THIS LIST ?
}
One method that I thought was to send the whole data as a query string to a servlet on the client side(running a server such as tomcat) and then stash that list onto some file.Later jsp can parse the file and display the data. But that doesn't seem to be a good option.
Note: JSP is invoked when a servlet forwards after successfully sending the IP to remote servlet
You can use request.setAttribute() in the Servlet. Then you can use a JSP tag to retrieve the value in JSP. Investigate a bit on that.
EDIT :
In the Servlet doGet method, you can set the an attribute, say listOfFiles as:
resquest.setAttribute("listOfFiles",list);
Then in the JSP you can retirieve it using the EL expression:
${listOfFiles}
which is an inbuilt feature of JSP.
Alternatively, you can access it using
<% request.getAttribute("listOfFiles")%>
but it is bad to embed Java scriptlets inside JSPs.
If you're passing complex data (lists etc.) over a connection, you can look into using JSON or XML. Your JSP code should be able to parse either easily with the right library.
Actually it depends on how the jsp is invoked.
Do you perform a post onto it ? Does the servlet perform a forward ? Or is a sendRedirect() ?
According to this there are many ways to send data to your jsp. One is using request attributes or better, using request scoped beans. One other can be posting some sort of rappresentation of your list as a post parameter (be it json, xml or some custom format of yours).
Another thing to consider is, what are you using ? JSF ? Some Spring library ? According to that there may be other better (or worse) ways to send datas.
If working with simple JSP/Servlet on a small project I'd personally go with the request.setAttribute() way, indeed this will force me to invoke the jsp via something like
request.setAttribute("myList", yourListObject);
request.getRequestDispatcher("yourjsp.jsp").forward(request, response);
then in the jsp you can:
<% Object myListObj = pageContext.getRequest().getAttribute("myList"); %>
When you call a servlet from code, the best way is to use an HttpServlet and mimic the browser behavior.
Passing the data in the URL is a GET request. Useful only with relatively small chunks of data, but the semantics look like you want ("get a bit of data"). Something like http://myremoteserver.com/myServlet?ip=.... You will need a proper encoding of the parameters (the Java API can handle that).
Passing the data by writting into the stream will be a POST request. It has no limit in the data passed to the server, but the semantics is different ("change something in the system"). You could pass your data as a parameter inside the contents written, I am not really sure how to decode that. Another alternative is using a Servlet (no HttpServlet) and just treat the raw data.
In both cases the response will be returned in the connection output stream. You can use whatever format you like (an standard one like JSON will probably be best, even that defining your own XML). In this case, a viable alternative would be filenames separated by a 'safe' character (like \n or |). Quick, but it will be less flexible in the future.
I would go for a GET request and JSON encoding.

Java Servlets, how to keep request attribute alive across several forwards?

I have a home-grown MVC implementation. A ControllerServlet like so:
/controller?cmd=EditUser&userid=55
From this URL, the controller creates a EditUserCommand.class instance and calls an execute() method which returns the result page (ex. user.jsp) to display.
The controller servlet then does a ...
getRequestDispatcher(resultPage).forward(request, response);
... and the resulting page is shown.
One of the things the controller does is set messages (error, info, and so on) as request attribtues. For example:
request.setAttribute("infoMessage", "User was edited successfully.");
And that message gets pulled out of the request in the user.jsp page and displayed.
Works fine.
Now here comes my problem.
Sometimes my commands don't return a page like user.jsp but return a URL like cmd=ShowUser&userid=55 for the result. This is because there might be things I want to check before displaying the final page, like permission to view the user and so on.
When I do this the "infoMessage" I placed in the request never appears because the result is a URL that makes a new call to the servlet, which is a new request. The new request doesn't maintain the request attributes from the first request; which makes sense, I just didn't forsee this happening.
How can I make my request variable "stay alive" until it's actually displayed on the final page that results from the original request?
Any suggestions or advice are appreciated. Just FYI, I can't re-write the entire app to go to something like Struts, Spring MVC, of JSF. It's not an option.
Thanks!
Rob
redirect generally looses request data because of brand new request from browser. One possible approach may be append your message to url string as attribute and read it when you need.
Based on your EDIT: After your edit also, my answer make sense. But, only one correction is, it is not brand new request because forward happens on server side.
Have you tried using a RequestDispatcher instead of a Redirect?
RequestDispatcher dispatcher = request.getRequestDispatcher("/myNextPage.jsp");
dispatcher.forward(request, response);
You may be giving a simple example, but your control flow causes the "Resend" Error, perhaps? Basically, after making any change to the data, the controller must immediately do a "Get" via a redirect and the screen should be displayed completely stateless.
Please take a look:
http://en.wikipedia.org/wiki/Post/Redirect/Get
As such, Attributes are not a great help. What you may want to consider is, maintaining a Bean/Object for every login user, and persist this object in a LRU cache (JCache or MemcacheD), and retrieve it on every entry to the application. Once you have that, you can maintain a pseudo-state such as previous results in that object.
In any case, using Attributes to retain state will severely constrain your options. You need to have a more generic flexible routing-independent mechanism.

Servlet 3.0 include html page

I'm trying to achieve the following behavior using the Servlet 3.0 API:
send an inital html page
send subsequent responses that update the page
This all works except the only way I could send the initial page without getting the response committed is by manually writing using the HttpResponse Writer...
I was wondering if there is a way of using something similar to RequestDispatcher#include with an html page without running into problems with the AsyncContext. Some things I tried until now and didn't work:
use AsyncContext#dispatch: as much as I read on the Internet, it is destined for sending the final response to the container in order to render it
use RequestDispatcher#forward: getting IllegalStateException due to trying to write more content in the OutputStream
use RequestDispatcher#include: if I initialize the AsyncContext before calling this method, request.isAsyncSupported returns true, after calling the method, it returns false. I read that it calls flushBuffer() and sets the commit flag to true on the response
Also, in the Servlet 3.0 spec there are some lines mentioning that dispatching from async servlet to normal servlet is possible but will commit the answer. I believe a static html page belongs to this category...
If you have any ideas of how an elegant include can be done without affecting the ability to still send streamed responses back to the client, please let me know.
Thanks
One solution (not the only one): if it's just a html page, then write the html page itself in html and do ajax calls to the serrvlet that needs to provide the updates.
use static elements on the page that store data and use requestdispatcher.
Or you could also just simply refresh the entire page with such an arrangement using response.setHeader("refresh", "5; URL=officer.html").
I really don't understand your need to send multiple requests without the response being committed to a servlet. are you trying to interact with a serving thread multiple times ?

Categories