How to dispatch a request depending on an exception? - java

I have a HTTP servlet that should redirect a user to a page depending on whether an object could be initialized properly or not.
Consider the following example:
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SomeClass sc;
try {
sc = new SomeClass(request);
} catch (SomeClassSomeException e) {
request.getRequestDispatcher("/Ex1.jsp").forward(request, response);
} catch (SomeClassAnotherException e) {
request.getRequestDispatcher("/Ex2.jsp").forward(request, response);
}
request.getRequestDispatcher("/Success.jsp").forward(request, response);
}
Is this the recommended way of doing this? Would another way of handling this be more appropriate?

Is this the recommended way of doing this?
It should work (modulo the issue below). In some contexts I would do it this way.
Would another way of handling this be more appropriate?
In other contexts (e.g. if I had lots of servlets which needed the same dispatching logic) I might do the dispatching of exceptions in a filter, or some other way.
There is one issue that you need to beware of. If the request processing has already "committed" the response before the exception is thrown, then forwarding to a JSP (or anything else) could be problematic. However, it looks like that can't happen ... given your current code.

You can try something like this:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SomeClass sc;
try {
sc = new SomeClass(request);
} catch (SomeClassSomeException e) {
request.getRequestDispatcher("/Ex1.jsp?error="+e).forward(request, response);
} catch (SomeClassAnotherException e) {
request.getRequestDispatcher("/Ex1.jsp?error="+e").forward(request, response);
}
request.getRequestDispatcher("/Success.jsp").forward(request, response);
}
On Ex1.jsp, you can use
<%= request.getParameter("error")%>
It will retireive the error message on the Ex1.jsp page.So a single page can handle all the error messages.
Hope this gives you a better approach to your solution.

Related

Bind different status code to HttpServletResponse in the servlet that handles uncaught exceptions

in my project I've defined a few types of exceptions that are supposed to be mapped to respective response status code
// map to status code 400
public class BadRequest400Exception extends RuntimeException {}
// map to status code 401
public class Unauthorized401Exception extends RuntimeException {}
// map to status code 404
public class NotFound404Exception extends RuntimeException {}
Please note that I do not want to catch these exceptions inside original servlets where they are thrown. That's why I make them unchecked. In other words, I don't want to have something like this
public class BusinessLogicServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
// some business logic
throw new Unauthorized401Exception();
} catch (Exception e) {
resp.sendError(401, "...");
}
}
which actually defeats my original purpose of processing these runtime exceptions all together in a central place. For that, I define a dedicated servlet for handling all uncaught exception thrown. It will forward the request to the appropriate error page based on type of exception
web.xml:
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/exception</location>
</error-page>
<!-- ... -->
<servlet>
<servlet-name>ExceptionCentralProcessingServlet</servlet-name>
<servlet-class>foo.bar.baz.ExceptionCentralProcessingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ExceptionCentralProcessingServlet</servlet-name>
<url-pattern>/WEB-INF/exception</url-pattern>
</servlet-mapping>
ExceptionCentralProcessingServlet:
public class ExceptionCentralProcessingServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (throwable instanceof BadRequest400Exception) {
// I want to change status code from 500 to 400 in this case
// which is not working
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
// also not working
// response.sendError(HttpServletResponse.SC_BAD_REQUEST);
request
.getRequestDispatcher("/WEB-INF/views/error-pages/error-page-400.jsp")
.forward(request, response);
return;
}
if (throwable instanceof NotFound404Exception) {
// I want to change status code from 500 to 404 in this case
// which is not working
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
// also not working
// response.sendError(HttpServletResponse.SC_NOT_FOUND);
request
.getRequestDispatcher("/WEB-INF/views/error-pages/error-page-404.jsp")
.forward(request, response);
return;
}
}
}
When I test it, this seems to be partially working. When BadRequest400Exception is thrown from a servlet, the request is forwarded to error-page-400.jsp. However, the response status code is still 500, despite that I've explicitly change it to 400 via response.setStatus(HttpServletResponse.SC_BAD_REQUEST);. According to Change response code in javax.servlet.http.HttpServlet, this is supposed to work but it actually doesn't work out.
I also try the other method sendError: response.sendError(400) but this is also not working.
I want to know how to change the http status code (default seems to be 500) to other values in the servlet declared for <exception-type> that processes uncaught exception thrown from other servlets.
Thanks
If your goal is to catch and handle exceptions in a centralized place outside of your main servlet, a simpler approach may be to use a filter. Filters are able to change the response status code.
A very basic example would look something like:
public class ExceptionFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
// nothing to do
}
#Override
public void destroy() {
// nothing to do
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
try {
chain.doFilter(request, response);
} catch BadRequest400Exception e) {
// however you want to handle this exception
} catch Unauthorized401Exception e) {
// however you want to handle this exception
}
// etc...
}
}
Note that you would also need to set up a corresponding filter-mapping in your web.xml.
In this example, the filter simply "wraps" the servlet request/response. Any uncaught exception that is thrown out of your servlet could be caught with the try/catch in the filter, such as to set a response status code, forward to a JSP, write to a log, etc.

Can you force a function not to return to the previous stack and just SendRedirect in a Java Servlet?

I'm trying to write error handling for a hibernate related project, I'm still new so there should be a better solution than this, but the way I did it was by creating a separate layer for running the hibernate queries
Now when handling hibernate exceptions in that layer I need to somehow sendRedirect without going back to the rest of the code as sendRedirect would throw an exception (forward and include could work but I would rather sendRedirect)
I tried to prevent the rest of the code to know about how I handle exceptions in that layer, but now I wonder if this was a good approach
Code sample of what I tried but doesn't seem to work as expected
#WebServlet("/signup")
public class RegistrationController extends HttpServlet {
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String email = request.getParameter("email");
String password = request.getParameter("password");
if (email != null && password != null) {
User user = new User(email, hashedPassword);
UserRepo userRepo = UserRepo.getInstance();
userRepo.create(user);
response.sendRedirect(UrlMappingConstants.getInstance().getControllerUrl(PageNames.SIGN_IN_PAGE));
}
}
}
And when something goes wrong in userRepo.create I call this in the catch
public void handleError() {
try {
ThreadLocalContext.forward(ServiceNames.ERROR_REDIRECT);
} catch (IOException | ServletException ioException) {
ioException.printStackTrace();
}
}
And all that does is redirect to error page
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendRedirect(UrlMappingConstants.getInstance().getControllerUrl(PageNames.TEST_JSP));
}

#MultiPartConfig Customized Message

In my Servlet I use the #MultiPartConfig annotation in combination with the maxFileSize attribute. This works as expected. The only issue I have is that I want to customize the Message (into Dutch). Is there a nice and clean way to achieve this?
My site is just a simple JSP.
You can customize message in application level
e.g. you can catch exception and throw new exception containing some message or print html in response or forward request to some error page.
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
Part filePart = req.getPart(FILE_PART_NAME);
}catch(Exception e) {
String msg=e.getMessage();
if(msg!=null) {
if(msg.contains("SRVE8021E")) {
throw new IllegalStateException("Etwas auf Deutsch sagen");
}
}

Calling service() of servlet from another servlet

I have a url-mapping in my web.xml such that requests for a specific url x.pt gets mapped to a Servlet say Servlet1. In the service() of this servlet I check if the request has some specific parameter.
If so, the call is delegated to another servlet Servlet2 by instantiating it and calling its service method.
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// if the call is for some special events (request has some specific parameter)
if (req.getParameter(conditionCheck()) {
doPost(req, res);
} else {
// Report parsing
}
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
try {
// instantiate Servlet2 object
servlet2.init(this.getServletConfig());
servlet2.service(req, res);
servlet2.destroy();
} catch (Exception e) {
LOG.error("Unable to execute event", e);
}
}
The browser returns some JSON text when the request is for special events( i.e. to Servlet2)
Do let me know if I need to do something extra for getting response of Servlet2 to the brwoser.
Thanks in advance!
You can forward your request using RequestDispacher:
RequestDispatcher rd = getServletContext().getRequestDispatcher(destination);
rd.forward(request, response);
As Kris says, I'd expect a RequestDispatcher to work, but I'm always uncomfortable when I see a servlet being called directly like this. Do you have the opportunity to move the logic that is provided by servlet2 into a separate object that both servlet1 and servlet2 can call upon? If you can, I think it'll give you a better, more easily testable solution.

Error Handler Servlet: how to get exception cause

I have an error servlet configured in my web.xml:
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/ExceptionHandler</location>
</error-page>
right?
In my (generically) servlet:
doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
...
...
} catch (Exception e) {
throw new ServletException("some mesage", e);
}
}
so, "e" will be the root cause in this case.
In my ExceptionHandler class, I have:
doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception");
throwable.getCause() //NULL
}
this is the problem. throwable.getCause() is null.
If the exception caught by the servletcontainer is a ServletException and the <error-page> is declared to catch an exception other than ServletException, then its cause will actually be unwrapped and stored as "javax.servlet.error.exception". So you basically already have it as throwable variable and you don't need to call getCause() on it.
See also 5th paragraph of chapter 9.9.2 of Servlet 2.5 specification:
If no error-page declaration containing an exception-type fits using the
class-hierarchy match, and the exception thrown is a ServletException or
subclass thereof, the container extracts the wrapped exception, as defined by the
ServletException.getRootCause method. A second pass is made over the error
page declarations, again attempting the match against the error page declarations,
but using the wrapped exception instead.
By the way, it's better to use the RequestDispatcher#ERROR_EXCEPTION constant instead of hardcoding it.
Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
EDITED.
Ok, this might be wrong, I do not have personal experience with error handling servlets: Instead of getCause(), add an instanceof check for ServletException, if it passes, cast your Throwable to ServletException and use getRootCause(). (BalusC seems to have a better solution, for newer Servlet API versions)
See Exceptions Without Root Cause for in-depth discussion.
Newer Servlet API versions do not have this issue, but if you are using some old version (2.4 or older) you should also update your ServletException throwing code:
doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
...
...
} catch (Exception e) {
ServletException se = new ServletException(e.getMessage(), e);
se.initCause(e);
throw se;
}
}

Categories