jamesfredley commented on PR #15541:
URL: https://github.com/apache/grails-core/pull/15541#issuecomment-4200435788

   ## JSP GrailsLayoutSpec Investigation - Root Cause Analysis
   
   ### The Failure
   
   The `"jsp demo"` test in `GrailsLayoutSpec` fails because the response is a 
completely empty HTML page (`<html><head></head><body></body></html>`). All 37 
other tests in the same spec pass (GSP-based views work fine).
   
   ### What Works
   
   1. Jasper initializes correctly - TLD scanning runs at startup
   2. The `GroovyPageViewResolver` correctly falls back to JSP when no GSP is 
found:
      ```
      Locating GSP view for controller DemoController and path /demo/hello
      No GSP view found, falling back to locating JSTL view for name 
[/demo/hello]
      ```
   3. The `JstlView` forwards to the correct path:
      ```
      Forwarding to [/WEB-INF/grails-app/views/demo/hello.jsp]
      ```
   4. The JSP compiles successfully via JDT:
      ```
      Compiled [.../hello_jsp.java] 773ms
      ```
   
   ### What Fails
   
   After JSP compilation and execution, the response body is empty. Even after 
removing the `<meta name="layout">` tag from the JSP (eliminating SiteMesh 
decoration entirely), the response remains empty. The JSP content is never 
captured.
   
   ### Root Cause
   
   The issue is in `grails-layout`'s response buffering pipeline. The rendering 
chain is:
   
   1. `EmbeddedGrailsLayoutView.renderTemplate()` calls `obtainContent()`
   2. `obtainContent()` creates a `GrailsContentBufferingResponse` wrapper and 
calls `innerView.render(model, request, contentBufferingResponse)`
   3. The `JstlView.render()` does a `RequestDispatcher.forward()` to the JSP, 
writing to the wrapped response
   4. `GrailsContentBufferingResponse.getContent()` tries to retrieve the 
captured content
   
   In step 4, `getContent()` returns `null`, causing the entire response to be 
silently dropped. This is likely because `GrailsPageResponseWrapper` (from 
SiteMesh 2.6.x) doesn't correctly capture output from Tomcat 11's forward 
dispatch. Tomcat 11 (Jakarta Servlet 6.1) may have changed how 
`RequestDispatcher.forward()` interacts with response wrappers, specifically 
around `getOutputStream()` vs `getWriter()` delegation, or response buffer 
commit semantics.
   
   ### Why GSP Works But JSP Doesn't
   
   GSP views write directly to the `GrailsContentBufferingResponse` via 
`GroovyPageView`, which uses `GSPGrailsLayoutPage` (captured at line 90-93 of 
`GrailsContentBufferingResponse.getContent()`). JSP views go through a 
`RequestDispatcher.forward()` which writes to the underlying 
`GrailsPageResponseWrapper` (captured at line 95-98). The forward dispatch path 
is what breaks in Tomcat 11.
   
   ### Fix Required
   
   This needs a fix in `grails-layout`'s `GrailsPageResponseWrapper` or 
`GrailsContentBufferingResponse` to properly capture content written during a 
`RequestDispatcher.forward()` on Tomcat 11 / Jakarta Servlet 6.1. The fix 
should be tracked as a separate issue since it's a `grails-layout` module bug, 
not a configuration problem.
   
   ### Impact
   
   Only JSP view rendering through the Grails layout pipeline is affected. Pure 
GSP views, REST responses, and direct `render text:` calls all work correctly.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to