I am working on an assignment that I have. It is fairly straight forward. An HTML form which contains a single input is submitted to a Servlet that grabs the parameter, creates a message based on the parameter, adds the message as an attribute to the request, and uses a requestdispatcher to forward to a jsp to display the message.
I have a requirement that if the parameter is missing, I need to display an error page. The catch is that I can't explicitly check for a null, or use a try/catch block. My guess is that the goal is to define an error page in the web.xml page to handle errors of a certain type, but the problem is, if I cant check to see if the request parameter is null, or use a try/catch, how do I know if I need to throw an exception? Any ideas?
If you intend to create a message based on the parameter then it is a bit difficult to see how you can achieve this if you cannot check the parameters value (for null for instance). Presumably you call...
HttpServletRequest.getParameter()
Which returns the parameter value or null if the parameter is missing.
In web.xml, you can mention exceptions also.
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error.jsp</location>
</error-page>
Or you can take help from this link to create new servlets to handle errors.
Servlet Exception Handling
Normally to check for null you'd do:
String param = request.getParameter("param");
if(param!=null)
If they don't want you doing that, probably they want you to use the DOT operator to cause the NullPointerExpection
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, NullPointerException
{
String param = request.getParameter("param");
if(param.equals("x"))
{
//if param was null, simply using
//the DOT operator on param would throw
// the NullPointerExpection
response.sendRedirect("x.jsp");
return;
}
}
To avoid having to explicitly check for null and avoid the NullPointerExpection you could do:
if("x".equals(param))
In the web.xml, you can specify an error page, like this.
Let's assume you want to catch HTTP400, 500 and exceptions:
<error-page>
<error-code>400</error-code>
<location>/errorpage.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/errorpage.html</location>
</error-page>
(as suggested by Arjit)
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/errorpage.html</location>
</error-page>
And then to put it all together, as suggested by DeveloperWJK, in the servlet:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, NullPointerException
{
String param = request.getParameter("param");
if(param.equals("x"))
{
response.sendRedirect("x.jsp");
return;
}
}
Related
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.
I have a web-app developed with servlet & JSP. I configured my app to throw an IllegalArgumentException if I insert bad parameters.
Then I configured my web.xml file in this way:
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
When I rise a 404 error, then it works and calls error.jsp, but when I rise a java.lang.IllegalArgumentException, then it does not work and I've a blank page instead of error.jsp. Why?
The server is Glassfish, and logs show really IllegalArgumentException rised.
You should not catch and suppress it, but just let it go.
I.e. do not do:
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
doSomethingWhichMayThrowException();
} catch (IllegalArgumentException e) {
e.printStackTrace(); // Or something else which totally suppresses the exception.
}
}
But rather just let it go:
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doSomethingWhichMayThrowException();
}
Or, if you actually intented to catch it for logging or so (I'd rather use a filter for that, but ala), then rethrow it:
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
doSomethingWhichMayThrowException();
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw e;
}
}
Or, if it's not an runtime exception, then rethrow it wrapped in ServletException, it will be automatically unwrapped by the container:
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
doSomethingWhichMayThrowException();
} catch (NotARuntimeException e) {
throw new ServletException(e);
}
}
See also:
How does server prioritize which type of web.xml error page to use?
Submitting form to Servlet which interacts with database results in blank page
Another (simplified) approach is not to declare multiple handlers for various <error-code> and <exception-type> situations but rather have one, sort of catch-all sink, e.g.
<error-page>
<location>/error-page.jsp</location>
</error-page>
Inside your error-page.jsp you can determine the cause, be it a return status code or an exception as described here: https://www.tutorialspoint.com/servlets/servlets-exception-handling.htm These constants are a part of the standard Servlet 3.0 API.
For instance a primitive error-page.jsp response handler placed into the root of your webapp can look like this:
Server encountered a situation
Status code: <%=(Integer) request.getAttribute(javax.servlet.RequestDispatcher.ERROR_STATUS_CODE)%>
<br>
Exception: <%=(Throwable) request.getAttribute(javax.servlet.RequestDispatcher.ERROR_EXCEPTION)%>
For security reasons I wouldn't recommend sending the exact exception type to the client; this is just an example of how to handle different types of errors and response statuses inside a JSP handler; a servlet can be used instead of JSP.
One common catch-all handler vs one per status code is certainly dependent on the situation and requirements.
I have today the same issue. (JavaEE 7 and Glassfish 4.0)
The problem seems that the framework check it as String instead with the Class.
String based check (the hypothesis)
When a Exception is twrown, e.getClass() is compared with <exception-type> as string.
So you can't use inheritance.
Note that nested classes must be pointed as '$' instead '.' (same as getClass() method).
Class based check
The framework create an instance of the class, and <exception-type> text refer to it, and the class.isInstance() is used to check.
This will need reflection and policy file could break it.
I hope that this response solves future issues.
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.
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.
This is pretty simple and straightforward. I want to throw a 503 error from the servlet side.
response.sendError(503);
When this is thrown, I need it to hit a custom error page. Basically a 503 error page itself, but with a few modifications.
Say I have 503.html, and I added
<error-page>
<error-code>503</error-code>
<location>/503.html</location>
</error-page>
in web.xml.
I created a war file, with a servlet which throws the 503 error, and web.xml with this content. I kept the 503.html in the parent folder location. (Should I keep it elsewhere ?)
I deployed the app in WLS, but this custom 503.html is not getting hit. I am getting the generic 503 error.
Am I missing something?
My code is below:
webapp1.war
->web-inf
->web-inf->classes->prject4->Class1.class
->web-inf->jsp->error->custom.html
web.xml
<?xml version="1.0"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>Class1</servlet-name>
<servlet-class>project2.Class1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Class1</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<error-page>
<error-code>503</error-code>
<location>/WEB-INF/jsp/error/custom.html</location>
</error-page>
</web-app>
class1.java
public class Class1 extends HttpServlet
{
private ServletConfig config;
public void init(ServletConfig config)throws ServletException
{
this.config=config;
}
public void service (HttpServletRequest request, HttpServletResponse response)
throws IOException
{
response.setContentType("text/html");
ServletOutputStream l_out = response.getOutputStream();
response.sendError(503);
}
}
Ok, this was a minor error which I didn't figure out in the beginning.
In my web.xml the servlet-mapping was given as /*, which was causing an infinite loop condition as it throws the same code for which it has been mapped. So I had to adjust the servlet mapping so that Class1 doesn't map to any error pages - like say /images/*.
And then everything started working fine. :)
You can also try handling it with custom Error Handler.
public void service (HttpServletRequest request, HttpServletResponse response)
throws IOException
{
try
{
//some error generating code
throw new Exception("503_Exception");
}
catch(Exception e)
{
response.sendRedirect(HandleError.handle(e, request));
}
}
A separate class to handle errors. This can handle different types of errors.
You can add functionality to log stacktrace, send out emails if something is wrong etc.
public class HandleError{
public static String handle(Throwable t, javax.servlet.http.HttpServletRequest request)
{
String sErrorMsg = t.getMessage();
if (sErrorMsg.equals("503_Exception")) {
request.setAttribute("msg", Constants.EINVALSESSION);
return "/503.html";
}
return "/default_error.html";
}
}
If you are using Maven as your project build tool then it will look in the src/main/webapp directory, so for example our config looks like this:
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error/error404.html</location>
</error-page>
and our error404.html sits in the folder:
${PROJECT_NAME}/src/main/webapp/WEB-INF/jsp/error/
If your not using Maven the path in the location will have a base directory of wherever you put your index.jsp
I guess there's a minimum limit on the number bytes your custom error page has. The lower limit is usually 512 Bytes. See Important note for your Custom error pages. I've seen this behavior in Google-Chrome too when using Tomcat.