Johan
Johan

Reputation: 40510

Show custom error page for PUT requests sent to JSP with Jetty 9.4

Background

When upgrading from Spring 4 to 5.3 the error page specified in web.xml no longer worked for certain HTTP verbs, one of these was PUT. For example, when a PUT request to a Spring controller resulted in an unexpected error, the error page defined in web.xml was not shown. Instead, we got an error along the lines of 405 Method Not Allowed. The error page was shown correctly for GET requests. The error page was (and is still) defined like this in web.xml:

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/uncaughtException</location>
</error-page>

"uncaughtException" is a tiles view defined like this:

<definition extends="siteDefault" name="uncaughtException">
     <put-attribute name="body" value="/WEB-INF/views/uncaughtException.jspx"/>
</definition>

To get this working for all HTTP verbs we added this Spring Controller:

@RequestMapping("/uncaughtException")
@Controller
public class UncaughtExceptionViewController {

    @RequestMapping
    public String uncaughtException() {
        return "uncaughtException";
    }

}

Now the error page was shown correctly even for PUT requests.

Previously we used Jetty 9.2 but when upgrading to Java 14 we were also forced to upgrade to Jetty 9.4. When doing this we noticed that the error page was no longer shown for PUT requests (it works for GET) even though we still have the UncaughtExceptionViewController controller. Instead, the 405 Method Not Allowed error is back. During debugging we noticed that the uncaughtException in UncaughtExceptionViewController is not called when using Jetty 9.4.

Question

How can we configure Jetty or Spring MVC to show the error page even for PUT requests and not only for GET and not show us 405 Method Not Allowed?

Update

After setting a break-point in org.eclipse.jetty.server.Response.setStatus I see that the problem seems to be that "JSPs only permit GET, POST or HEAD. Jasper also permits OPTIONS". The full stack-trace is:

sendError:454, Response (org.eclipse.jetty.server)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:119, OnCommittedResponseWrapper (org.springframework.session.web.http)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:119, OnCommittedResponseWrapper (org.springframework.security.web.util)
sendError:158, HttpServletResponseWrapper (javax.servlet.http)
sendError:119, OnCommittedResponseWrapper (org.springframework.security.web.util)
_jspService:1, site_005fdefault_jspx (org.apache.jsp.WEB_002dINF.layouts)
service:71, HttpJspBase (org.apache.jasper.runtime)
service:790, HttpServlet (javax.servlet.http)
service:476, JspServletWrapper (org.apache.jasper.servlet)
serviceJspFile:386, JspServlet (org.apache.jasper.servlet)
service:330, JspServlet (org.apache.jasper.servlet)
service:106, JettyJspServlet (org.eclipse.jetty.jsp)
service:790, HttpServlet (javax.servlet.http)
service:1402, ServletHolder$NotAsyncServlet (org.eclipse.jetty.servlet)
handle:763, ServletHolder (org.eclipse.jetty.servlet)
doHandle:569, ServletHandler (org.eclipse.jetty.servlet)
handle:143, ScopedHandler (org.eclipse.jetty.server.handler)
handle:620, SecurityHandler (org.eclipse.jetty.security)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
nextHandle:235, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1610, SessionHandler (org.eclipse.jetty.server.session)
nextHandle:233, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1377, ContextHandler (org.eclipse.jetty.server.handler)
nextScope:188, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:507, ServletHandler (org.eclipse.jetty.servlet)
doScope:1580, SessionHandler (org.eclipse.jetty.server.session)
nextScope:186, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:1292, ContextHandler (org.eclipse.jetty.server.handler)
handle:141, ScopedHandler (org.eclipse.jetty.server.handler)
forward:219, Dispatcher (org.eclipse.jetty.server)
forward:78, Dispatcher (org.eclipse.jetty.server)
forward:407, SessionRepositoryFilter$SessionRepositoryRequestWrapper$SessionCommittingRequestDispatcher (org.springframework.session.web.http)
forward:265, ServletRequest (org.apache.tiles.request.servlet)
doForward:228, ServletRequest (org.apache.tiles.request.servlet)
dispatch:57, AbstractClientRequest (org.apache.tiles.request)
render:47, DispatchRenderer (org.apache.tiles.request.render)
render:259, BasicTilesContainer (org.apache.tiles.impl)
render:397, BasicTilesContainer (org.apache.tiles.impl)
render:238, BasicTilesContainer (org.apache.tiles.impl)
render:221, BasicTilesContainer (org.apache.tiles.impl)
render:59, DefinitionRenderer (org.apache.tiles.renderer)
renderMergedOutputModel:147, TilesView (org.springframework.web.servlet.view.tiles3)
render:316, AbstractView (org.springframework.web.servlet.view)
render:1373, DispatcherServlet (org.springframework.web.servlet)
processDispatchResult:1118, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1057, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doPut:920, FrameworkServlet (org.springframework.web.servlet)
service:710, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:790, HttpServlet (javax.servlet.http)
handle:763, ServletHolder (org.eclipse.jetty.servlet)
doFilter:1651, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilter:226, WebSocketUpgradeFilter (org.eclipse.jetty.websocket.server)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:186, OpenEntityManagerInViewFilter (org.springframework.orm.jpa.support)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilter:317, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
invoke:127, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:91, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:114, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:137, SessionManagementFilter (org.springframework.security.web.session)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:111, AnonymousAuthenticationFilter (org.springframework.security.web.authentication)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:158, RememberMeAuthenticationFilter (org.springframework.security.web.authentication.rememberme)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:170, SecurityContextHolderAwareRequestFilter (org.springframework.security.web.servletapi)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:63, RequestCacheAwareFilter (org.springframework.security.web.savedrequest)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:158, BasicAuthenticationFilter (org.springframework.security.web.authentication.www)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:200, AbstractAuthenticationProcessingFilter (org.springframework.security.web.authentication)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:116, LogoutFilter (org.springframework.security.web.authentication.logout)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:66, HeaderWriterFilter (org.springframework.security.web.header)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:56, WebAsyncManagerIntegrationFilter (org.springframework.security.web.context.request.async)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:105, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:331, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:214, FilterChainProxy (org.springframework.security.web)
doFilter:177, FilterChainProxy (org.springframework.security.web)
invokeDelegate:358, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:271, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:94, HiddenHttpMethodFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:40, CorsFilter (com.mycompany.spring)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doFilterInternal:141, SessionRepositoryFilter (org.springframework.session.web.http)
doFilter:82, OncePerRequestFilter (org.springframework.session.web.http)
invokeDelegate:358, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:271, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:1638, ServletHandler$CachedChain (org.eclipse.jetty.servlet)
doHandle:567, ServletHandler (org.eclipse.jetty.servlet)
handle:143, ScopedHandler (org.eclipse.jetty.server.handler)
handle:578, SecurityHandler (org.eclipse.jetty.security)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
nextHandle:235, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1610, SessionHandler (org.eclipse.jetty.server.session)
nextHandle:233, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1377, ContextHandler (org.eclipse.jetty.server.handler)
nextScope:188, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:507, ServletHandler (org.eclipse.jetty.servlet)
doScope:1580, SessionHandler (org.eclipse.jetty.server.session)
nextScope:186, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:1292, ContextHandler (org.eclipse.jetty.server.handler)
handle:141, ScopedHandler (org.eclipse.jetty.server.handler)
handle:191, ContextHandlerCollection (org.eclipse.jetty.server.handler)
handle:146, HandlerCollection (org.eclipse.jetty.server.handler)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
handle:501, Server (org.eclipse.jetty.server)
lambda$handle$1:383, HttpChannel (org.eclipse.jetty.server)
dispatch:-1, 435181768 (org.eclipse.jetty.server.HttpChannel$$Lambda$1064)
dispatch:556, HttpChannel (org.eclipse.jetty.server)
handle:375, HttpChannel (org.eclipse.jetty.server)
onFillable:273, HttpConnection (org.eclipse.jetty.server)
succeeded:311, AbstractConnection$ReadCallback (org.eclipse.jetty.io)
fillable:105, FillInterest (org.eclipse.jetty.io)
run:104, ChannelEndPoint$1 (org.eclipse.jetty.io)
runTask:336, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
doProduce:313, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
tryProduce:171, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
produce:135, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
run:-1, 976266910 (org.eclipse.jetty.io.ManagedSelector$$Lambda$1056)
runJob:806, QueuedThreadPool (org.eclipse.jetty.util.thread)
run:938, QueuedThreadPool$Runner (org.eclipse.jetty.util.thread)
run:832, Thread (java.lang)

Upvotes: 0

Views: 600

Answers (1)

Joakim Erdfelt
Joakim Erdfelt

Reputation: 49462

Since this is produced by JSP.

The standard Servlet Error-Page handling can be used.

The WEB-INF/web.xml can be defined to respond to status code 405.

  <error-page>
    <error-code>405</error-code>
    <location>/myMethodNotAllowedPath</location>
  </error-page>

You can also, optionally, define a global error page error handler like this ..

  <error-page>
    <location>/myGlobalErrorHandler</location>
  </error-page>

Since Servlet 3.0, the <error-page> can define a reaction based on a status code <error-code>, an exception <exception-type>, or nothing (which means all errors not caught by more specific definitions)

The Servlet implementation will redispatch the request to the defined <location> with DispatcherType.ERROR, and the details of the original request can be found in the HttpServletRequest.getAttribute(String) values under the various keys/names defined in the RequestDispatcher.ERROR_* constants.

Upvotes: 1

Related Questions