Skip to content

Remove the hardcoded servlet API dependency from gspCompile - #16068

Merged
codeconsole merged 4 commits into
apache:8.0.xfrom
codeconsole:fix/gspcompile-bom-managed-servlet-api
Aug 2, 2026
Merged

Remove the hardcoded servlet API dependency from gspCompile#16068
codeconsole merged 4 commits into
apache:8.0.xfrom
codeconsole:fix/gspcompile-bom-managed-servlet-api

Conversation

@codeconsole

@codeconsole codeconsole commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What

GroovyPagePlugin declared jakarta.servlet:jakarta.servlet-api:6.0.0 on the gspCompile configuration it registers. This PR removes the dependency entirely. The gspCompile configuration is kept as an extension point for additional GSP-compile-only dependencies; empty, it resolves trivially.

Why it's redundant

The servlet API already reaches the compile classpath transitively wherever GSPs can compile at all:

  • grails-web-gsp exposes grails-web-taglib via api
  • grails-web-taglib exposes jakarta.servlet:jakarta.servlet-api via compileOnlyApi (needed for the TagLibrary trait)
  • (grails-console contributes a second route via a plain api dependency in apps)

Since compiling a GSP definitionally requires the grails-gsp artifacts on the classpath, the servlet API always tags along — BOM-versioned (spring-boot-dependencies → 6.1.0 today), with jakarta-servlet.version property overrides working via BomPropertyOverridesPlugin.

Empirical verification

A bare Grails plugin project — groovy + grails-plugin + grails-gsp plugins, implementation 'org.apache.grails:grails-gsp' + grails-web-boot, one GSP exercising params/request/session and g:each/g:link/g:form, no servlet-api declaration anywhere — compiles its GSPs successfully against published 8.0.0-M4 artifacts with this dependency deleted. dependencyInsight confirms jakarta.servlet-api:6.1.0 arrives on compileClasspath purely via the transitive routes above, and the gspCompile configuration resolves empty.

Why the hardcode was worth removing

  • It had drifted: pinned 6.0.0 while the Spring Boot 4 BOM manages 6.1.0 — any hardcoded version here needs a manual bump on servlet-level changes, and nobody remembers.
  • It registered an explicitly-versioned dependency that version-scanning tooling (e.g. the ben-manes versions plugin with declared-only filtering) reports as if the build had declared it, which is how this was noticed.
  • With no dependency at all, the grails { bom = null } and standalone-grails-gsp cases need nothing: there is no version to supply and nothing to resolve.

Verified with :grails-gradle-plugins:compileGroovy and :grails-gradle-plugins:test, plus the external probe project above.

@codeconsole
codeconsole marked this pull request as draft July 30, 2026 04:56
@codeconsole codeconsole changed the title Manage gspCompile's servlet API via the BOM instead of hardcoding 6.0.0 Remove the hardcoded servlet API dependency from gspCompile Jul 30, 2026
GroovyPagePlugin declared jakarta.servlet:jakarta.servlet-api:6.0.0 on the
gspCompile configuration it registers. The dependency is redundant: the
servlet API reaches the compile classpath transitively wherever GSPs can
compile at all - grails-web-gsp exposes grails-web-taglib via api, and
grails-web-taglib exposes jakarta.servlet-api via compileOnlyApi
(grails-console contributes a second api route in apps). Since compiling a
GSP definitionally requires the grails-gsp artifacts on the classpath, the
servlet API always tags along, BOM-versioned (spring-boot-dependencies ->
6.1.0 today) with jakarta-servlet.version property overrides applying via
BomPropertyOverridesPlugin.

The hardcoded version had also drifted (6.0.0 vs the 6.1.0 the Spring Boot 4
BOM manages) and registered an explicitly-versioned dependency that
version-scanning tooling reports as if the build had declared it, which is
how this was noticed.

Empirically verified: a bare Grails plugin project (grails-plugin +
grails-gsp plugins, one GSP using params/request/session and g: taglibs, no
servlet-api declaration anywhere) compiles its GSPs successfully against
published 8.0.0-M4 artifacts with this dependency deleted; dependencyInsight
confirms the transitive routes above and gspCompile resolves empty.

The gspCompile configuration is kept as an extension point for additional
GSP-compile-only dependencies.
@codeconsole
codeconsole force-pushed the fix/gspcompile-bom-managed-servlet-api branch from 2fa647e to aba633b Compare July 30, 2026 15:37
@codeconsole
codeconsole marked this pull request as ready for review July 30, 2026 16:25
@jdaugherty

jdaugherty commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

I'm ok to merge this if @matrei is. I think he had been making changes to not rely on transitive dependencies. GSP's project structure is odd to me though so maybe it's explicit due it's split. I want his approval to merge this.

@matrei

matrei commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

@jdaugherty @codeconsole We can remove it, but we should also add it to grails-web-gsp as compileOnlyApi. Otherwise we are depending on grails-web-taglib to always be included, which it does not have to be.

We should also add a comment in the GroovyPagePlugin where we are registering the gspCompile configuration, that it is an extension point for applications to add compile dependencies for GSP compilation. I can't think of a use case for that right now, and if there is no use case, we could remove the gspCompile configuration all together.

Addresses review feedback on the gspCompile servlet-api removal.

grails-web-gsp uses jakarta.servlet types throughout its own public API
(GroovyPageView, PageRenderer, GroovyPagesServlet, GSPResponseWriter,
NullView, GroovyPageViewResolver) but declared no servlet-api dependency:
it compiled only because grails-web-taglib re-exports one via
compileOnlyApi, reached through the api project(':grails-web-taglib')
edge. Consumers that exclude grails-web-taglib would lose the servlet API
from the GSP compile classpath. Declare compileOnlyApi
'jakarta.servlet:jakarta.servlet-api' on grails-web-gsp directly so the
module stands on its own; the version stays BOM-managed (6.1.0), and the
generated POM now exports it from grails-web-gsp itself.

Also document the gspCompile configuration at its registration site: it
is appended to the GSP compile classpath alongside compileClasspath, so
applications can use it for classes referenced from GSPs but absent from
their own compile classpath. It is empty by default now that the
hardcoded servlet-api entry is gone.

Verified with :grails-web-gsp:build, :grails-web-taglib:build,
validateDependencyVersions on both, :grails-gradle-plugins:check, and
builds of every in-repo consumer of grails-web-gsp.
@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.9586%. Comparing base (3d129b7) to head (ea59fc5).

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##                8.0.x     #16068        +/-   ##
==================================================
- Coverage     51.9800%   51.9586%   -0.0214%     
+ Complexity      18035      18027         -8     
==================================================
  Files            2038       2038                
  Lines           95708      95707         -1     
  Branches        16648      16648                
==================================================
- Hits            49749      49728        -21     
- Misses          38622      38643        +21     
+ Partials         7337       7336         -1     
Files with missing lines Coverage Δ
...ls/gradle/plugin/views/gsp/GroovyPagePlugin.groovy 0.0000% <ø> (ø)

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@testlens-app

testlens-app Bot commented Aug 2, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: ea59fc5
▶️ Tests: 66022 executed
⚪️ Checks: 62/62 completed


Learn more about TestLens at testlens.app.

@codeconsole
codeconsole merged commit 063ca52 into apache:8.0.x Aug 2, 2026
65 checks passed
jdaugherty pushed a commit that referenced this pull request Aug 3, 2026
Follow-up to #16068, which removed the hardcoded servlet API dependency
that gspCompile carried.

The configuration was introduced in 2014 as the classpath for the
original Ant-based GSP compiler, which ran via IsolatedAntBuilder:

    antBuilder.withClasspath(project.configurations.gspCompile)

It held the two artifacts that compiler needed - grails-web-gsp and the
servlet API. When GSP compilation moved to the forked
GroovyPageForkCompileTask, whose classpath is compileClasspath +
gspCompile + compiled classes + providedCompile, the grails-web-gsp entry
was dropped and only the servlet API line remained. #16068 removed that,
leaving a configuration that is empty for every build and contributes no
files to the GSP compile classpath.

GSPs continue to compile against compileClasspath, the project's compiled
classes and providedCompile, which is what actually supplies everything
GSP compilation needs.

This is a behavior break for any build that declares dependencies on
gspCompile: those fail with "Could not find method gspCompile()".
Documented in the Grails 8 upgrade guide with the migration to
compileOnly, which lands on the same compile classpath.

Adds GroovyPagePluginFunctionalSpec, a TestKit spec that applies
org.apache.grails.gradle.grails-gsp and asserts the configuration is gone
while compileGroovyPages and compileWebappGroovyPages still resolve the
compile classpath, provided dependencies and compiled classes. Verified
the first assertion fails when the configuration is restored.

Verified with :grails-gradle-plugins:test --rerun-tasks (90 tests),
:grails-gradle-plugins:check, aggregateViolations (Checkstyle, CodeNarc,
PMD, SpotBugs all clean) and :grails-doc:publishGuide.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants