codeconsole commented on code in PR #16499:
URL: https://github.com/apache/grails-core/pull/16499#discussion_r4178176905


##########
grails-forge/grails-forge-core/src/main/resources/gsp/index.gsp:
##########
@@ -577,25 +577,57 @@
                 <g:def type="List" var="mimeTypeProviders"
                        
value="${applicationContext.getBeansOfType(grails.web.mime.MimeTypeProvider)
                                .entrySet().toList().sort { 
it.key.toLowerCase() }}"/>
-                <%-- The filters still on the call stack ARE this request's 
pipeline, in
-                     execution order: walk the reversed stack, keep Filter 
classes, collapse
-                     the extra frames a filter contributes through its 
abstract bases, and
-                     number what remains. No registry can report this actual 
order. --%>
-                <g:def type="List" var="requestFilters"
-                       
value="${Thread.currentThread().stackTrace.toList().reverse()
-                               .findResults { ste ->
-                                   def cls = null
-                                   try { cls = Class.forName(ste.className, 
false, Thread.currentThread().contextClassLoader) } catch (Throwable ignored) { 
}
-                                   (cls != null && 
jakarta.servlet.Filter.isAssignableFrom(cls)) ? cls : null
-                               }
-                               .inject([]) { acc, cls ->
-                                   Class prev = acc ? (Class) acc[-1] : null
-                                   if (prev == cls) { return acc }
-                                   if (prev != null && 
prev.isAssignableFrom(cls)) { acc[-1] = cls; return acc }
-                                   if (prev != null && 
cls.isAssignableFrom(prev)) { return acc }
-                                   acc << cls
-                               }
-                               .unique()}"/>
+                <%-- Every filter running in the container, in chain order. 
Tomcat's filter
+                     maps are the exact chain: FilterRegistrationBeans, plain 
Filter beans Boot
+                     adapted and container-added filters like WsFilter alike. 
Elsewhere, replay
+                     the order Boot registers its filters in 
(ServletContextInitializerBeans,
+                     disabled ones skipped, matchAfter ones last), then append 
what else the
+                     Servlet API reports, unnumbered because no portable API 
exposes its
+                     position. Either way the spec chains URL-pattern matches 
before
+                     servlet-name matches, hence the stable sorts. --%>
+                <g:set var="tomcatContext"
+                       value="${ { ->
+                           try {
+                               
applicationContext.webServer.tomcat.host.findChildren().find { it.path == 
request.contextPath }
+                           } catch (Throwable ignored) {
+                               null
+                           }
+                       }() }"/>
+                <g:def type="List" var="servletFilters"
+                       value="${tomcatContext
+                               ? tomcatContext.findFilterMaps().toList()
+                                   .inject([:]) { Map acc, fm ->

Review Comment:
   Fixed in 6eacfd59f6. The panel now lists one row per filter map instead of 
grouping by filter: the URL-pattern maps first, then the servlet-name maps 
under their own heading, numbered in the order Tomcat walks them. With maps `A 
-> dispatcherServlet`, `B -> /*`, `A -> /*` it lists B's URL map before A's, 
and A again under servlet names. A filter mapped more than once can run at 
different positions for different requests, so the numbers are per mapping 
rather than per filter. `WelcomePageFiltersSpec` in the new `welcome-page` test 
example renders exactly that case on Tomcat.



##########
grails-profiles/web/skeleton/grails-app/views/index.gsp:
##########
@@ -577,25 +577,57 @@
                 <g:def type="List" var="mimeTypeProviders"
                        
value="${applicationContext.getBeansOfType(grails.web.mime.MimeTypeProvider)
                                .entrySet().toList().sort { 
it.key.toLowerCase() }}"/>
-                <%-- The filters still on the call stack ARE this request's 
pipeline, in
-                     execution order: walk the reversed stack, keep Filter 
classes, collapse
-                     the extra frames a filter contributes through its 
abstract bases, and
-                     number what remains. No registry can report this actual 
order. --%>
-                <g:def type="List" var="requestFilters"
-                       
value="${Thread.currentThread().stackTrace.toList().reverse()
-                               .findResults { ste ->
-                                   def cls = null
-                                   try { cls = Class.forName(ste.className, 
false, Thread.currentThread().contextClassLoader) } catch (Throwable ignored) { 
}
-                                   (cls != null && 
jakarta.servlet.Filter.isAssignableFrom(cls)) ? cls : null
-                               }
-                               .inject([]) { acc, cls ->
-                                   Class prev = acc ? (Class) acc[-1] : null
-                                   if (prev == cls) { return acc }
-                                   if (prev != null && 
prev.isAssignableFrom(cls)) { acc[-1] = cls; return acc }
-                                   if (prev != null && 
cls.isAssignableFrom(prev)) { return acc }
-                                   acc << cls
-                               }
-                               .unique()}"/>
+                <%-- Every filter running in the container, in chain order. 
Tomcat's filter
+                     maps are the exact chain: FilterRegistrationBeans, plain 
Filter beans Boot
+                     adapted and container-added filters like WsFilter alike. 
Elsewhere, replay
+                     the order Boot registers its filters in 
(ServletContextInitializerBeans,
+                     disabled ones skipped, matchAfter ones last), then append 
what else the
+                     Servlet API reports, unnumbered because no portable API 
exposes its
+                     position. Either way the spec chains URL-pattern matches 
before
+                     servlet-name matches, hence the stable sorts. --%>
+                <g:set var="tomcatContext"
+                       value="${ { ->
+                           try {
+                               
applicationContext.webServer.tomcat.host.findChildren().find { it.path == 
request.contextPath }
+                           } catch (Throwable ignored) {
+                               null
+                           }
+                       }() }"/>
+                <g:def type="List" var="servletFilters"
+                       value="${tomcatContext
+                               ? tomcatContext.findFilterMaps().toList()
+                                   .inject([:]) { Map acc, fm ->

Review Comment:
   Fixed in the same commit, 6eacfd59f6. The two copies are still identical.



##########
grails-forge/grails-forge-core/src/main/resources/gsp/index.gsp:
##########
@@ -577,25 +577,57 @@
                 <g:def type="List" var="mimeTypeProviders"
                        
value="${applicationContext.getBeansOfType(grails.web.mime.MimeTypeProvider)
                                .entrySet().toList().sort { 
it.key.toLowerCase() }}"/>
-                <%-- The filters still on the call stack ARE this request's 
pipeline, in
-                     execution order: walk the reversed stack, keep Filter 
classes, collapse
-                     the extra frames a filter contributes through its 
abstract bases, and
-                     number what remains. No registry can report this actual 
order. --%>
-                <g:def type="List" var="requestFilters"
-                       
value="${Thread.currentThread().stackTrace.toList().reverse()
-                               .findResults { ste ->
-                                   def cls = null
-                                   try { cls = Class.forName(ste.className, 
false, Thread.currentThread().contextClassLoader) } catch (Throwable ignored) { 
}
-                                   (cls != null && 
jakarta.servlet.Filter.isAssignableFrom(cls)) ? cls : null
-                               }
-                               .inject([]) { acc, cls ->
-                                   Class prev = acc ? (Class) acc[-1] : null
-                                   if (prev == cls) { return acc }
-                                   if (prev != null && 
prev.isAssignableFrom(cls)) { acc[-1] = cls; return acc }
-                                   if (prev != null && 
cls.isAssignableFrom(prev)) { return acc }
-                                   acc << cls
-                               }
-                               .unique()}"/>
+                <%-- Every filter running in the container, in chain order. 
Tomcat's filter
+                     maps are the exact chain: FilterRegistrationBeans, plain 
Filter beans Boot
+                     adapted and container-added filters like WsFilter alike. 
Elsewhere, replay
+                     the order Boot registers its filters in 
(ServletContextInitializerBeans,
+                     disabled ones skipped, matchAfter ones last), then append 
what else the
+                     Servlet API reports, unnumbered because no portable API 
exposes its
+                     position. Either way the spec chains URL-pattern matches 
before
+                     servlet-name matches, hence the stable sorts. --%>
+                <g:set var="tomcatContext"
+                       value="${ { ->
+                           try {
+                               
applicationContext.webServer.tomcat.host.findChildren().find { it.path == 
request.contextPath }
+                           } catch (Throwable ignored) {
+                               null
+                           }
+                       }() }"/>
+                <g:def type="List" var="servletFilters"
+                       value="${tomcatContext
+                               ? tomcatContext.findFilterMaps().toList()
+                                   .inject([:]) { Map acc, fm ->
+                                       Map row = 
acc.computeIfAbsent(fm.filterName) { n ->
+                                           [name: n, className: 
tomcatContext.findFilterDef(n)?.filterClass ?: '', urlPatterns: [], mappings: 
[], ordered: true]
+                                       }
+                                       row.urlPatterns.addAll(fm.URLPatterns)
+                                       row.mappings.addAll(fm.URLPatterns)
+                                       row.mappings.addAll(fm.servletNames)
+                                       acc
+                                   }
+                                   .values().toList()
+                                   .sort { it.urlPatterns ? 0 : 1 }
+                               : { ->
+                                   List springFilters = new 
org.springframework.boot.web.servlet.ServletContextInitializerBeans(
+                                               
(org.springframework.beans.factory.ListableBeanFactory) 
applicationContext).toList()
+                                           .findAll { it instanceof 
org.springframework.boot.web.servlet.AbstractFilterRegistrationBean && 
it.enabled }
+                                           .collect { initializer ->
+                                               def rb = 
(org.springframework.boot.web.servlet.AbstractFilterRegistrationBean) 
initializer
+                                               List servletNames = 
(rb.servletNames as List) + rb.servletRegistrationBeans*.servletName
+                                               List urlPatterns = 
(rb.urlPatterns || servletNames) ? rb.urlPatterns as List : ['/*']
+                                               [name: rb.filterName, 
className: rb.filter?.getClass()?.name ?: '', urlPatterns: urlPatterns,
+                                                mappings: urlPatterns + 
servletNames, matchAfter: rb.matchAfter, ordered: true]
+                                           }
+                                           .sort { (it.urlPatterns ? 0 : 2) + 
(it.matchAfter ? 1 : 0) }

Review Comment:
   Fixed in 6eacfd59f6. Nothing is numbered off Tomcat anymore. The panel lists 
Spring Boot's enabled registrations in registration order, then the container's 
other filters, under a separate description (`welcome.filters.unordered`) 
saying that is not necessarily the order they run. 
`WelcomePageFiltersJettySpec` in the new `welcome-page-jetty` test example 
checks it on Jetty.



##########
grails-profiles/web/skeleton/grails-app/views/index.gsp:
##########
@@ -577,25 +577,57 @@
                 <g:def type="List" var="mimeTypeProviders"
                        
value="${applicationContext.getBeansOfType(grails.web.mime.MimeTypeProvider)
                                .entrySet().toList().sort { 
it.key.toLowerCase() }}"/>
-                <%-- The filters still on the call stack ARE this request's 
pipeline, in
-                     execution order: walk the reversed stack, keep Filter 
classes, collapse
-                     the extra frames a filter contributes through its 
abstract bases, and
-                     number what remains. No registry can report this actual 
order. --%>
-                <g:def type="List" var="requestFilters"
-                       
value="${Thread.currentThread().stackTrace.toList().reverse()
-                               .findResults { ste ->
-                                   def cls = null
-                                   try { cls = Class.forName(ste.className, 
false, Thread.currentThread().contextClassLoader) } catch (Throwable ignored) { 
}
-                                   (cls != null && 
jakarta.servlet.Filter.isAssignableFrom(cls)) ? cls : null
-                               }
-                               .inject([]) { acc, cls ->
-                                   Class prev = acc ? (Class) acc[-1] : null
-                                   if (prev == cls) { return acc }
-                                   if (prev != null && 
prev.isAssignableFrom(cls)) { acc[-1] = cls; return acc }
-                                   if (prev != null && 
cls.isAssignableFrom(prev)) { return acc }
-                                   acc << cls
-                               }
-                               .unique()}"/>
+                <%-- Every filter running in the container, in chain order. 
Tomcat's filter
+                     maps are the exact chain: FilterRegistrationBeans, plain 
Filter beans Boot
+                     adapted and container-added filters like WsFilter alike. 
Elsewhere, replay
+                     the order Boot registers its filters in 
(ServletContextInitializerBeans,
+                     disabled ones skipped, matchAfter ones last), then append 
what else the
+                     Servlet API reports, unnumbered because no portable API 
exposes its
+                     position. Either way the spec chains URL-pattern matches 
before
+                     servlet-name matches, hence the stable sorts. --%>
+                <g:set var="tomcatContext"
+                       value="${ { ->
+                           try {
+                               
applicationContext.webServer.tomcat.host.findChildren().find { it.path == 
request.contextPath }
+                           } catch (Throwable ignored) {
+                               null
+                           }
+                       }() }"/>
+                <g:def type="List" var="servletFilters"
+                       value="${tomcatContext
+                               ? tomcatContext.findFilterMaps().toList()
+                                   .inject([:]) { Map acc, fm ->
+                                       Map row = 
acc.computeIfAbsent(fm.filterName) { n ->
+                                           [name: n, className: 
tomcatContext.findFilterDef(n)?.filterClass ?: '', urlPatterns: [], mappings: 
[], ordered: true]
+                                       }
+                                       row.urlPatterns.addAll(fm.URLPatterns)
+                                       row.mappings.addAll(fm.URLPatterns)
+                                       row.mappings.addAll(fm.servletNames)
+                                       acc
+                                   }
+                                   .values().toList()
+                                   .sort { it.urlPatterns ? 0 : 1 }
+                               : { ->
+                                   List springFilters = new 
org.springframework.boot.web.servlet.ServletContextInitializerBeans(
+                                               
(org.springframework.beans.factory.ListableBeanFactory) 
applicationContext).toList()
+                                           .findAll { it instanceof 
org.springframework.boot.web.servlet.AbstractFilterRegistrationBean && 
it.enabled }
+                                           .collect { initializer ->
+                                               def rb = 
(org.springframework.boot.web.servlet.AbstractFilterRegistrationBean) 
initializer
+                                               List servletNames = 
(rb.servletNames as List) + rb.servletRegistrationBeans*.servletName
+                                               List urlPatterns = 
(rb.urlPatterns || servletNames) ? rb.urlPatterns as List : ['/*']
+                                               [name: rb.filterName, 
className: rb.filter?.getClass()?.name ?: '', urlPatterns: urlPatterns,
+                                                mappings: urlPatterns + 
servletNames, matchAfter: rb.matchAfter, ordered: true]
+                                           }
+                                           .sort { (it.urlPatterns ? 0 : 2) + 
(it.matchAfter ? 1 : 0) }

Review Comment:
   Same fix, 6eacfd59f6. The copies are identical.



##########
grails-forge/grails-forge-core/src/main/resources/gsp/index.gsp:
##########
@@ -577,25 +577,57 @@
                 <g:def type="List" var="mimeTypeProviders"
                        
value="${applicationContext.getBeansOfType(grails.web.mime.MimeTypeProvider)
                                .entrySet().toList().sort { 
it.key.toLowerCase() }}"/>
-                <%-- The filters still on the call stack ARE this request's 
pipeline, in
-                     execution order: walk the reversed stack, keep Filter 
classes, collapse
-                     the extra frames a filter contributes through its 
abstract bases, and
-                     number what remains. No registry can report this actual 
order. --%>
-                <g:def type="List" var="requestFilters"
-                       
value="${Thread.currentThread().stackTrace.toList().reverse()
-                               .findResults { ste ->
-                                   def cls = null
-                                   try { cls = Class.forName(ste.className, 
false, Thread.currentThread().contextClassLoader) } catch (Throwable ignored) { 
}
-                                   (cls != null && 
jakarta.servlet.Filter.isAssignableFrom(cls)) ? cls : null
-                               }
-                               .inject([]) { acc, cls ->
-                                   Class prev = acc ? (Class) acc[-1] : null
-                                   if (prev == cls) { return acc }
-                                   if (prev != null && 
prev.isAssignableFrom(cls)) { acc[-1] = cls; return acc }
-                                   if (prev != null && 
cls.isAssignableFrom(prev)) { return acc }
-                                   acc << cls
-                               }
-                               .unique()}"/>
+                <%-- Every filter running in the container, in chain order. 
Tomcat's filter
+                     maps are the exact chain: FilterRegistrationBeans, plain 
Filter beans Boot
+                     adapted and container-added filters like WsFilter alike. 
Elsewhere, replay
+                     the order Boot registers its filters in 
(ServletContextInitializerBeans,
+                     disabled ones skipped, matchAfter ones last), then append 
what else the
+                     Servlet API reports, unnumbered because no portable API 
exposes its
+                     position. Either way the spec chains URL-pattern matches 
before
+                     servlet-name matches, hence the stable sorts. --%>
+                <g:set var="tomcatContext"
+                       value="${ { ->
+                           try {
+                               
applicationContext.webServer.tomcat.host.findChildren().find { it.path == 
request.contextPath }
+                           } catch (Throwable ignored) {
+                               null
+                           }
+                       }() }"/>
+                <g:def type="List" var="servletFilters"
+                       value="${tomcatContext
+                               ? tomcatContext.findFilterMaps().toList()
+                                   .inject([:]) { Map acc, fm ->
+                                       Map row = 
acc.computeIfAbsent(fm.filterName) { n ->
+                                           [name: n, className: 
tomcatContext.findFilterDef(n)?.filterClass ?: '', urlPatterns: [], mappings: 
[], ordered: true]
+                                       }
+                                       row.urlPatterns.addAll(fm.URLPatterns)
+                                       row.mappings.addAll(fm.URLPatterns)
+                                       row.mappings.addAll(fm.servletNames)

Review Comment:
   Fixed in 6eacfd59f6. The Tomcat rows read `matchAllUrlPatterns` and 
`matchAllServletNames` and show the mapping as `*`, so a match-all URL mapping 
is listed with the URL-pattern maps. The Tomcat test example registers one.



##########
grails-forge/grails-forge-core/src/test/groovy/org/grails/forge/feature/view/GrailsGspSpec.groovy:
##########
@@ -250,11 +250,13 @@ class GrailsGspSpec extends ApplicationContextSpec 
implements CommandOutputFixtu
         index.contains('mappingContext.eventListeners')
         index.contains('<g:message code="welcome.datastores.listeners"/>')
 
-        and: "the request's effective filter pipeline is derived from the 
rendering call stack"
+        and: "servlet filters list every filter in the container in chain 
order, with a portable fallback"
         index.contains('data-switch-type="filters"')
-        index.contains('Thread.currentThread().stackTrace')
-        index.contains('jakarta.servlet.Filter.isAssignableFrom')
-        index.contains('<g:message code="welcome.filters.request"/>')
+        index.contains('tomcatContext.findFilterMaps()')

Review Comment:
   Agreed. b8f8c251f7 adds two test examples, `welcome-page` (Tomcat) and 
`welcome-page-jetty`, that stage the web profile's welcome page, compile it 
statically and render it in development, where the filter panels appear. Their 
filters cover a servlet-name map ahead of another filter's URL map, a match-all 
mapping, a disabled registration and a filter added straight to the container. 
Both specs fail against the previous template, and the Jetty spec also fails 
with the disabled-registration check removed. The string checks here stay only 
as a quick structural check of the template.



-- 
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