So i got this problem, i do a servlet project using tomcat and I got this class that should handle the exception that was thrown and then display the jsp with error status code.
public class ErrorHandler extends HttpServlet {
private static final Logger LOGGER = Logger.getLogger(ErrorHandler.class);
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Throwable throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
Integer statusCode = (Integer) req.getAttribute("javax.servlet.error.status_code");
if (throwable != null) {
LOGGER.fatal("Exception: " + throwable);
}
if (statusCode != null) {
LOGGER.fatal("Error, status code: " + statusCode);
}
req.getRequestDispatcher("error.jsp").forward(req, resp);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
This handler is mapped in the web.xml this way.
<servlet-mapping>
<servlet-name>ErrorHandler</servlet-name>
<url-pattern>/error</url-pattern>
</servlet-mapping>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error</location>
</error-page>
The question is, why do i keep getting default tomcat exception page, instead of mine. My logger works just fine, so it's clear, that after exception was happened, method doGet is called, but when it comes to request dispatcher, it just doesn't work. My jsp pages are placed in webapp folder, near WEB-INF.
Because the request dispatcher for error.jsp is just a dispatcher. Java/tomat isn't going to realize that error.jsp means you intended for this to be the error handler. It's just a page, just like foo.jsp. So, that dispatcher picks up the call, so to speak, notices that the request involves an error state, and it, in turn, forwards the request to the error dispatcher.
Related
I am trying to write the JUnit test case for the code:
In SearchController class
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<AlbumSimplified> items = spotifyService.searchAlbum(searchName);
request.setAttribute("items", items);
request.getRequestDispatcher("searchResult.jsp").forward(request, response);
}
as
public void SearchControllerTesting() throws ServletException, IOException {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
//mocked myalbums
when(searchServiceMock.searchAlbum(anyString())).thenReturn(myalbums); when(request.getRequestDispatcher(anyString())).thenReturn(request.getRequestDispatcher("searchResult.jsp"));
searchController.doGet(request, response);
}
The error I am facing is:
java.lang.NullPointerException: Cannot invoke "jakarta.servlet.RequestDispatcher.forward(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse)" because the return value of "jakarta.servlet.http.HttpServletRequest.getRequestDispatcher(String)" is null
I believe that it is due to the fact that the uri is not identified for the request and so, it is not able to find the "searchResult.jsp" located at "/app/src/main/webapp/searchResult.jsp" where app is the root of the project directory.
Hence I tried to set the
when(request.getRequestedURI()).thenReturn("app/search"), which is the URL of the request in the browser for non-testing usage.
However, I am not able to move ahead and solve the issue.
I want the items in the request to go to the searchResult.jsp, and return me a response of type "text/html".
Thanks.
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.
A JSP page named Test.jsp is mapped to the following Servlet.
#WebServlet(name = "TestServlet", urlPatterns = {"/TestServlet"})
public final class TestServlet extends HttpServlet
{
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//request.getRequestDispatcher("/WEB-INF/admin_side/Test.jsp").forward(request, response);
response.sendRedirect("TestServlet");
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
#Override
public String getServletInfo() {
return "Short description";
}
}
This Servlet is mapped to a JSP page Test.jsp. The doGet() method is invoked, when a URL like http://localhost:8080/Assignment/TestServlet is entered in the address bar.
The request can be forwarded to the given URL as commented out. Is it possible to redirect to the same JSP page, Test.jsp?
If an attempt is made to do so, Google Chrome complains,
This webpage has a redirect loop
It can however, redirect to other pages under WEB-INF/admin_side.
The POST-REDIRECT-GET pattern works like so: a client sends a POST request, your server handles it and responds with a redirect, ie. a response with a 302 status code and Location header to the appropriate URI. The client makes a GET request to that URI.
Currently, your server is redirecting on both GET and POSTS requests. What's worse is that your GET is redirecting to the same URI that it is handling, creating the redirect loop you are seeing.
Change your Servlet implementation so that the POST sends a redirect, but the GET actually serves up a normal 200 response with HTML, AJAX, etc.
This question already has answers here:
HTTP Status 405 - HTTP method is not supported by this URL
(2 answers)
Closed 9 years ago.
public class RoarHistoryUpdate extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
super.doGet(request, response);
System.out.println("do Get");
response.setContentType("text/html");
response.getOutputStream().print("Success");
}
}
This is my Servlet. And it is registerd in the web.xml like this:
<servlet>
<display-name>RoarHistoryUpdateServlet</display-name>
<servlet-name>RoarHistoryUpdateServlet</servlet-name>
<servlet-class>de.ulm.uni.vs.avid.roary.servlets.RoarHistoryUpdate</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RoarHistoryUpdateServlet</servlet-name>
<url-pattern>/Roary/UpdateServlet</url-pattern>
</servlet-mapping>
When I go to the URL http://localhost:8080/Roary-JSP/Roary/UpdateServlet It says HTTP Status 405 - HTTP method GET is not supported by this URL
Funny thing is I get the do Get logged to my console. So it actually found the doGet-method.
I am using a GlassFish Server Open Source Edition 3.1.2.2
Because when you do super.doGet(request, response); in your Servlet's doGet() method, you actually call the doget() of the HttpServlet class. The Tomcat 7 implementation of that method is as below (may be a similar implementation exists for Glassfish):
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
My guess is that it's because of invoking super.doGet(). If you check the source code of HttpServlet, you'll see that it sets this status code on the response. So drop the super call. It's not needed.
I'm using the channel API in Java runtime. The servlet I have mapped to /_ah/channel/connected does not appear to be running. I am creating a channel, passing the token, and opening it on the server. This works fine. I do see the call to /_ah/channel/connected in my log, however no log messages appear and the code does not appear to be running. Below is my code and web.xml
ChannelConnectedServlet.java:
public class ChannelConnectedServlet extends HttpServlet{
private static final Logger logger = Logger.getLogger(ChannelConnectedServlet.class
.getName());
private void process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
logger.log(Level.WARNING,"test");
//do stuff here
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
logger.log(Level.WARNING,"Channel connected!");
process(req, resp);
}
}
web.xml:
<servlet-mapping>
<servlet-name>ChannelConnected</servlet-name>
<url-pattern>/_ah/channel/connected</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ChannelConnected</servlet-name>
<servlet-class>com.myapp.server.channel.ChannelConnectedServlet</servlet-class>
</servlet>
The same behavior happens with the disconnect request. HELP!!!
This entry in web.xml should have included "/" at the end of the url, such as:
<servlet-mapping>
<servlet-name>ChannelConnected</servlet-name>
<url-pattern>/_ah/channel/connected/</url-pattern>
Works now.