You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2024/03/02 13:08:18 UTC

(tapestry-5) branch master updated: TAP5-2741: switch to Jakarta EE (#43)

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git


The following commit(s) were added to refs/heads/master by this push:
     new fe3fabc6e TAP5-2741: switch to Jakarta EE (#43)
fe3fabc6e is described below

commit fe3fabc6e1814b958a8bf1b675907504be2c2ddb
Author: Christian Köberl <12...@users.noreply.github.com>
AuthorDate: Sat Mar 2 14:08:13 2024 +0100

    TAP5-2741: switch to Jakarta EE (#43)
    
    Things changed:
    - Switch all Java EE to Jakarta EE APIs
    - Update references in JavaDoc for Jakarta EE
    - Switch to Spring 6 (Jakarta EE, Java 17)
    - Switch to Jetty 11 and Tomcat 10.1 (for support of Jakarta EE)
---
 build.gradle                                       |  58 ++++----
 .../internal/plastic/PlasticInternalUtils.java     |  12 ++
 quickstart/build.gradle                            |   8 +-
 .../archetype-resources/build-spring-boot.gradle   |   2 +-
 .../archetype-resources/build.gradle               |   2 +-
 .../archetype-resources/pom-spring-boot.xml        |   4 +-
 .../resources-filtered/archetype-resources/pom.xml |   6 +-
 .../src/main/java/spring/AppConfiguration.java     |   8 +-
 tapestry-beanvalidator/build.gradle                |   7 +-
 .../beanvalidator/BeanValidatorConfigurer.java     |   6 +-
 .../beanvalidator/BeanValidatorSource.java         |   8 +-
 .../beanvalidator/ClientConstraintDescriptor.java  |   4 +-
 .../beanvalidator/modules/BeanValidatorModule.java |  12 +-
 .../internal/beanvalidator/BeanFieldValidator.java |  52 ++++---
 .../BeanFieldValidatorDefaultSource.java           |   4 +-
 .../beanvalidator/BeanValidatorSourceImpl.java     |  10 +-
 .../beanvalidator/MessageInterpolatorImpl.java     |   6 +-
 .../example/testapp/entities/BeanForTAP1981.java   |   2 +-
 .../org/example/testapp/entities/ComplexBean.java  |   4 +-
 .../testapp/entities/SomeOtherSimpleBean.java      |   2 +-
 .../example/testapp/entities/SomeSimpleBean.java   |   2 +-
 .../org/example/testapp/entities/TestEntity.java   |   2 +-
 .../java/org/example/testapp/entities/User.java    |   6 +-
 .../testapp/pages/FormClientValidationDemo.java    |   6 +-
 .../example/testapp/pages/FormValidationDemo.java  |   2 +-
 .../example/testapp/pages/InjectValidatorDemo.java |   6 +-
 .../org/example/testapp/services/AppModule.java    |   2 +-
 .../resources/ValidationMessages_en.properties     |  28 ++--
 tapestry-clojure/build.gradle                      |   4 +-
 tapestry-core/build.gradle                         |  10 +-
 .../java/org/apache/tapestry5/SymbolConstants.java |  56 ++++----
 .../java/org/apache/tapestry5/TapestryFilter.java  |   2 +-
 .../tapestry5/corelib/components/Palette.java      |   4 +-
 .../internal/services/AssetDispatcher.java         |   2 +-
 .../tapestry5/internal/services/CookieSink.java    |   2 +-
 .../tapestry5/internal/services/CookieSource.java  |   2 +-
 .../tapestry5/internal/services/CookiesImpl.java   |   2 +-
 .../services/DefaultRequestExceptionHandler.java   |   2 +-
 .../internal/services/IgnoredPathsFilter.java      |   4 +-
 .../internal/services/ResourceStreamer.java        |   2 +-
 .../internal/services/ResourceStreamerImpl.java    |   2 +-
 .../internal/services/StaticFilesFilter.java       |   2 +-
 .../UnknownActivationContextHandlerImpl.java       |   2 +-
 .../services/javascript/ModuleDispatcher.java      |   2 +-
 .../rest/DefaultOpenApiDescriptionGenerator.java   |   2 +-
 .../internal/test/TestableCookieSinkSource.java    |   2 +-
 .../internal/test/TestableRequestImpl.java         |   3 +-
 .../internal/test/TestableResponseImpl.java        |  27 ++--
 .../apache/tapestry5/modules/InternalModule.java   |   2 +-
 .../tapestry5/services/DelegatingRequest.java      |   7 +-
 .../org/apache/tapestry5/services/HttpStatus.java  |   2 +-
 .../services/assets/AssetRequestHandler.java       |   2 +-
 .../apache/tapestry5/test/TapestryTestCase.java    |   8 +-
 .../integration/app5/pages/Error404.groovy         |   2 +-
 .../ResponseCompressionAnalyzerTest.groovy         |   2 +-
 .../javascript/ModuleDispatcherTests.groovy        |   2 +-
 .../integration/app1/base/BaseRestDemoPage.java    |   2 +-
 .../FormFragmentExplicitVisibleBoundsDemo.java     |   3 +-
 .../integration/app1/pages/ReturnTypes.java        |   2 +-
 .../app2/pages/TestPageForHttpError.java           |   2 +-
 .../integration/cluster/ClusterTests.java          |  22 ++-
 .../tapestry5/integration/rest/RestTests.java      |   2 +-
 .../tapestry5/internal/ResponseImplTest.java       |   4 +-
 .../internal/ServletContextSymbolProviderTest.java |   2 +-
 .../internal/services/ContextImplTest.java         |   2 +-
 .../internal/services/CookiesImplTest.java         |   2 +-
 .../internal/services/IgnoredPathsFilterTest.java  |   4 +-
 .../internal/services/RequestImplTest.java         |   4 +-
 .../internal/services/RestSupportImplTest.java     |  51 ++++---
 .../internal/services/SessionImplTest.java         |   4 +-
 .../internal/services/StaticFilesFilterTest.java   |   2 +-
 .../internal/test/TestableResponseImplTest.java    |   2 +-
 .../apache/tapestry5/services/HttpStatusTest.java  |   2 +-
 tapestry-http/build.gradle                         |   6 +-
 .../apache/tapestry5/http/AsyncRequestHandler.java |   6 +-
 .../http/AsyncRequestHandlerResponse.java          |  54 +++----
 .../apache/tapestry5/http/CorsHandlerResult.java   |  14 +-
 .../org/apache/tapestry5/http/TapestryFilter.java  |  20 +--
 .../http/TapestryHttpSymbolConstants.java          |  32 ++---
 .../http/internal/AsyncRequestService.java         |   4 +-
 .../internal/ServletContextSymbolProvider.java     |   2 +-
 .../TypeCoercerHttpRequestBodyConverter.java       |   4 +-
 .../internal/gzip/BufferedGZipOutputStream.java    |  18 ++-
 .../http/internal/gzip/GZIPEnabledResponse.java    |   8 +-
 .../tapestry5/http/internal/gzip/GZipFilter.java   |   4 +-
 .../internal/services/ApplicationGlobalsImpl.java  |   2 +-
 .../internal/services/AsyncRequestServiceImpl.java |   4 +-
 .../internal/services/ClusteredSessionImpl.java    |   4 +-
 .../http/internal/services/ContextImpl.java        |   2 +-
 .../internal/services/CorsHandlerHelperImpl.java   |   4 +-
 .../http/internal/services/DefaultCorsHandler.java |   4 +-
 .../http/internal/services/RequestGlobalsImpl.java |   4 +-
 .../http/internal/services/RequestImpl.java        |   5 +-
 .../services/ResponseCompressionAnalyzerImpl.java  |   2 +-
 .../http/internal/services/ResponseImpl.java       |   4 +-
 .../http/internal/services/RestSupportImpl.java    |   2 +-
 .../http/internal/services/SessionImpl.java        |   4 +-
 .../services/TapestrySessionFactoryImpl.java       |   4 +-
 .../tapestry5/http/modules/TapestryHttpModule.java |   6 +-
 .../http/services/ApplicationGlobals.java          |   2 +-
 .../tapestry5/http/services/CorsHandler.java       |   4 +-
 .../tapestry5/http/services/CorsHandlerHelper.java |   4 +-
 .../services/CorsHttpServletRequestFilter.java     |   4 +-
 .../http/services/HttpRequestBodyConverter.java    |   2 +-
 .../http/services/HttpServletRequestFilter.java    |   4 +-
 .../http/services/HttpServletRequestHandler.java   |   4 +-
 .../tapestry5/http/services/RequestGlobals.java    |   4 +-
 .../tapestry5/http/services/RestSupport.java       |   2 +-
 .../services/ServletApplicationInitializer.java    |   2 +-
 .../ServletApplicationInitializerFilter.java       |   2 +-
 .../apache/tapestry5/http/services/Session.java    |   2 +-
 .../services/CorsHandlerHelperImplTest.java        | 156 +++++++++++----------
 .../services/CorsHttpServletRequestFilterTest.java |   4 +-
 .../internal/services/DefaultCorsHandlerTest.java  |   4 +-
 .../http/test/TestHttpServletRequestFilter.java    |   4 +-
 tapestry-http/src/test/webapp/longfile.txt         |  12 +-
 tapestry-kaptcha/build.gradle                      |   4 +-
 .../tapestry5/kaptcha/components/KaptchaImage.java |   2 +-
 tapestry-latest-java-tests/build.gradle            |   2 +-
 tapestry-openapi-viewer/build.gradle               |   2 +-
 tapestry-rest-jackson/build.gradle                 |   2 +-
 .../internal/JacksonHttpRequestBodyConverter.java  |   2 +-
 .../org/apache/tapestry5/test/JettyRunner.java     |  17 ++-
 tapestry-spring/build.gradle                       |  10 +-
 .../internal/spring/CustomizingContextLoader.java  |   2 +-
 .../tapestry5/internal/spring/SpringModuleDef.java |   2 +-
 .../spring/ApplicationContextCustomizer.java       |   2 +-
 .../tapestry5/spring/TapestrySpringFilter.java     |   2 +-
 .../tapestry5/spring/modules/SpringModule.java     |   2 +-
 .../spring/CustomizingContextLoaderTest.java       |   2 +-
 .../internal/spring/SpringModuleDefTest.java       |   2 +-
 .../apache/tapestry5/spring/SpringTestCase.java    |   2 +-
 .../spring/TapestryApplicationContextTest.java     |   2 +-
 .../standard/TapestrySpringIntegrationTest.java    |   2 +-
 .../org/example/testapp/services/AppModule.java    |   2 +-
 tapestry-upload/build.gradle                       |   6 +-
 .../internal/services/MultipartDecoderImpl.java    |  22 +--
 .../services/MultipartServletRequestFilter.java    |   8 +-
 .../services/ParametersServletRequestWrapper.java  |   4 +-
 .../internal/services/UploadExceptionFilter.java   |   6 +-
 .../upload/internal/services/UploadedFileItem.java |  13 +-
 .../tapestry5/upload/modules/UploadModule.java     |  11 +-
 .../upload/services/MultipartDecoder.java          |   5 +-
 .../tapestry5/upload/services/UploadEvents.java    |   4 +-
 .../services/MultipartDecoderImplTest.java         |  15 +-
 .../MultipartServletRequestFilterTest.java         |   4 +-
 .../ParametersServletRequestWrapperTest.java       |   2 +-
 .../upload/internal/services/StubFileItem.java     |  25 ++--
 .../internal/services/UploadedFileItemTest.java    |   6 +-
 .../test/java/org/example/upload/pages/Start.java  |   2 +-
 150 files changed, 631 insertions(+), 548 deletions(-)

diff --git a/build.gradle b/build.gradle
index 7397347b7..52de8ac94 100755
--- a/build.gradle
+++ b/build.gradle
@@ -10,11 +10,11 @@ apply from: "md5.gradle"
 apply from: "sha256.gradle"
 
 project.ext.versions = [
-    jetty: "8.1.19.v20160209",
-    tomcat: "7.0.70",
+    jetty: "11.0.16",
+    tomcat: "10.1.13",
     testng: "6.14.3",
     easymock: "3.3.1",
-    servletapi: "3.0.1",
+    servletapi: "5.0.0",
     spock: "2.1-M2-groovy-3.0",
     hibernate: "5.4.32.Final",
     slf4j: "1.7.25",
@@ -106,7 +106,7 @@ allprojects {
 
 idea {
     project {
-        languageLevel = "1.8"
+        languageLevel = "8"
     }
 }
 
@@ -137,7 +137,7 @@ String jdkVersion = System.properties['java.version']
 def jdkMajorVersion = jdkVersion.substring(0, jdkVersion.indexOf(".")) // 1, 9, 10...
 
 subprojects {
-    
+
     def specifyMaxPermSize = jdkVersion ==~ /1\.[67].+/
 
     apply plugin: "maven-publish"  // for deployment
@@ -153,13 +153,13 @@ subprojects {
     apply plugin: "project-report"
     apply plugin: "jacoco"
     apply plugin: "base"
-    
+
     jacoco {
         toolVersion = "0.8.7"
     }
-    
-    sourceCompatibility = "1.8"
-    targetCompatibility = "1.8"
+
+    sourceCompatibility = "8"
+    targetCompatibility = "8"
 
     // See http://jira.codehaus.org/browse/GRADLE-784
 
@@ -180,11 +180,11 @@ subprojects {
     eclipse.classpath.plusConfigurations += [configurations.provided]
 
     dependencies {
-        
-        // https://docs.gradle.org/7.3.3/userguide/upgrading_version_6.html#potential_breaking_changes, 
+
+        // https://docs.gradle.org/7.3.3/userguide/upgrading_version_6.html#potential_breaking_changes,
         // Table 1. Common configuration upgrades
         testImplementation "org.spockframework:spock-core:${versions.spock}"
-         
+
         testRuntimeOnly "org.slf4j:slf4j-log4j12:${versions.slf4j}"
     }
 
@@ -208,12 +208,12 @@ subprojects {
         }else{
           maxHeapSize "600M"
         }
-    
+
         // Needed to have XMLTokenStreamTests.testStreamEncoding() passing on Java 9+
         if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_1_9)) {
             jvmArgs("--add-opens=java.base/java.nio.charset=ALL-UNNAMED");
         }
-    
+
         // Turn off live service reloading
 
         systemProperties["tapestry.service-reloading-enabled"] = "false"
@@ -222,10 +222,10 @@ subprojects {
         jvmArgs("-Dfile.encoding=UTF-8")
 
         environment.LANG = 'en_US.UTF-8'
-        
+
         if (continuousIntegrationBuild) {
           // Travis runs our builds with TERM=dumb and kills it if we don't produce any
-          // output for 10 minutes, so we log some task execution progress 
+          // output for 10 minutes, so we log some task execution progress
 
           testLogging {
             exceptionFormat "full"
@@ -251,13 +251,13 @@ subprojects {
         	attributes("Automatic-Module-Name": "org.apache.tapestry." + projectDir.getName()
         		.replaceAll("tapestry5", "tapestry")
         		.replaceAll("tapestry-", "")
-        		.replaceAll("-", ""))        		
+        		.replaceAll("-", ""))
         	if (projectDir.getName().equals("tapestry-version-migrator")) {
         		attributes("Main-Class": "org.apache.tapestry5.versionmigrator.Main")
         	}
         }
     }
-    
+
     assemble.dependsOn(processResources, compileJava, jar)
 
     task sourcesJar(type: Jar) {
@@ -283,7 +283,7 @@ subprojects {
             published.extendsFrom signatures
         }
     }
-    
+
     publishing {
         publications {
             mavenJava(MavenPublication) {
@@ -291,11 +291,11 @@ subprojects {
                 groupId = "org.apache.tapestry"
                 from components.java
                 artifact sourcesJar
-                
+
                 pom {
                     name = project.name
                     // TODO: find some way to get the subproject description here.
-                    // description = 
+                    // description =
                     url = "https://tapestry.apache.org/"
                     licenses {
                         license {
@@ -337,7 +337,7 @@ subprojects {
                         }
                     }
                 }
-                
+
             }
         }
     }
@@ -369,7 +369,7 @@ subprojects {
         resolutionStrategy.force "antlr:antlr:2.7.7",
             "cglib:cglib-nodep:2.2",
             "commons-codec:commons-codec:1.10",
-            "commons-io:commons-io:2.4",
+            "commons-io:commons-io:2.13.0",
             "commons-logging:commons-logging:1.1.3",
             "hsqldb:hsqldb:2.2.8",
             "org.antlr:antlr-runtime:3.5.2",
@@ -423,7 +423,7 @@ task aggregateJavadoc(type: Javadoc) {
     exclude "org/apache/tapestry5/internal/plastic/asm/**"
     exclude "org/apache/tapestry5/internal/webresources/**"
     exclude "org/apache/tapestry5/webresources/modules/**"
-    
+
     source allMainJavaFiles
 
     classpath += files(allMainSourceSets*.compileClasspath)
@@ -674,7 +674,7 @@ task updateBootstrap {
 	  def bootstrapVersion = '3.3.7'
 	  def target = new File(temporaryDir, 'bootstrap.zip')
 	  ant.get(src: "https://github.com/twbs/bootstrap/archive/v${bootstrapVersion}.zip", dest: target)
-	
+
 	  def adjustDirectory = {
 	      def relativePath = it.relativePath
 	      if (relativePath.pathString.contains('/dist/')){
@@ -684,9 +684,9 @@ task updateBootstrap {
 	      }
 	      println "copying $it.relativePath to $relativePath"
 	      it.relativePath = relativePath
-	
+
 	  }
-	
+
 	  copy {
 	    from(zipTree(target)){
 	        include('*/js/*.js')
@@ -702,13 +702,13 @@ task updateBootstrap {
 	    }
 	    into('tapestry-core/src/main/resources/META-INF/assets/tapestry5/bootstrap/')
 	  }
-	
+
 	  copy {
 	    from(zipTree(target)){
 	        include('*/js/*.js')
 	        include('*/dist/fonts/*')
 	        include('*/less/**/*.less')
-	
+
 	        eachFile adjustDirectory
 	    }
 	    into('tapestry-webresources/src/test/webapp/bootstrap/')
diff --git a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java
index 2a337e49c..562ff027e 100644
--- a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java
+++ b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticInternalUtils.java
@@ -428,6 +428,18 @@ public class PlasticInternalUtils
 
         ClassVisitor adapter = new ClassVisitor(Opcodes.ASM9, result)
         {
+            @Override
+            public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value)
+            {
+                // we need to remove final from non-static fields because otherwise we get:
+                // IllegalAccessError: update to non-static final field
+                if ((access & Opcodes.ACC_STATIC) == 0)
+                {
+                    access &= ~Opcodes.ACC_FINAL;
+                }
+                return super.visitField(access, name, descriptor, signature, value);
+            }
+
             @Override
             public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
             {
diff --git a/quickstart/build.gradle b/quickstart/build.gradle
index 0bde4d343..a9a5357ce 100644
--- a/quickstart/build.gradle
+++ b/quickstart/build.gradle
@@ -50,17 +50,17 @@ task processFiltered(type: Copy) {
     filter(ReplaceTokens, tokens: [
         quickstartVersion: project.parent.version,
         tapestryVersion: project.parent.version,
-        springBootVersion: '2.5.4',
+        springBootVersion: '3.1.3',
         junitVersion: '5.8.2',
         jacksonVersion: '2.13.1',
         jsonVersion: '1.1.4',
         log4jVersion: '2.17.2',
         yassonVersion: '2.0.4',
-        servletVersion: '3.1.0',
+        servletVersion: '5.0.0',
         mavenCompilerVersion: '3.8.1',
-        mavenSurefireVersion: '3.0.0-M5',
+        mavenSurefireVersion: '3.1',
         mavenWarVersion: '3.3.1',
-        mavenJettyVersion: '10.0.6',
+        mavenJettyVersion: '11.0.15',
         grettyVersion: '3.0.6'
     ])
 }
diff --git a/quickstart/src/main/resources-filtered/archetype-resources/build-spring-boot.gradle b/quickstart/src/main/resources-filtered/archetype-resources/build-spring-boot.gradle
index 810b91d19..7d363ca57 100644
--- a/quickstart/src/main/resources-filtered/archetype-resources/build-spring-boot.gradle
+++ b/quickstart/src/main/resources-filtered/archetype-resources/build-spring-boot.gradle
@@ -74,7 +74,7 @@ dependencies {
     runtimeOnly("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}")
     runtimeOnly("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${versions.jackson}")
 
-    runtimeOnly("javax.servlet:javax.servlet-api:${versions.servlet}")
+    runtimeOnly("jakarta.servlet:jakarta.servlet-api:${versions.servlet}")
 }
 
 sourceSets {
diff --git a/quickstart/src/main/resources-filtered/archetype-resources/build.gradle b/quickstart/src/main/resources-filtered/archetype-resources/build.gradle
index 207be4041..1806bdb31 100644
--- a/quickstart/src/main/resources-filtered/archetype-resources/build.gradle
+++ b/quickstart/src/main/resources-filtered/archetype-resources/build.gradle
@@ -58,7 +58,7 @@ dependencies {
     runtimeOnly("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}")
     runtimeOnly("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${versions.jackson}")
 
-    runtimeOnly("javax.servlet:javax.servlet-api:${versions.servlet}")
+    runtimeOnly("jakarta.servlet:jakarta.servlet-api:${versions.servlet}")
 }
 
 sourceSets {
diff --git a/quickstart/src/main/resources-filtered/archetype-resources/pom-spring-boot.xml b/quickstart/src/main/resources-filtered/archetype-resources/pom-spring-boot.xml
index d7b74a6e4..c48e87aca 100644
--- a/quickstart/src/main/resources-filtered/archetype-resources/pom-spring-boot.xml
+++ b/quickstart/src/main/resources-filtered/archetype-resources/pom-spring-boot.xml
@@ -152,8 +152,8 @@
             <version>${D}{jackson-version}</version>
         </dependency>
         <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
             <version>${D}{servlet-version}</version>
             <scope>provided</scope>
         </dependency>
diff --git a/quickstart/src/main/resources-filtered/archetype-resources/pom.xml b/quickstart/src/main/resources-filtered/archetype-resources/pom.xml
index a1214b5ef..55f2a09b4 100644
--- a/quickstart/src/main/resources-filtered/archetype-resources/pom.xml
+++ b/quickstart/src/main/resources-filtered/archetype-resources/pom.xml
@@ -133,8 +133,8 @@
             <version>${D}{jackson-version}</version>
         </dependency>
         <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
             <version>${D}{servlet-version}</version>
             <scope>provided</scope>
         </dependency>
@@ -165,7 +165,7 @@
 			    <groupId>org.apache.maven.plugins</groupId>
 			    <artifactId>maven-war-plugin</artifactId>
 			    <version>${maven-war-version}</version>
-			</plugin>            
+			</plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
diff --git a/quickstart/src/main/resources/archetype-resources/src/main/java/spring/AppConfiguration.java b/quickstart/src/main/resources/archetype-resources/src/main/java/spring/AppConfiguration.java
index 7eebc90ae..a7af4497b 100644
--- a/quickstart/src/main/resources/archetype-resources/src/main/java/spring/AppConfiguration.java
+++ b/quickstart/src/main/resources/archetype-resources/src/main/java/spring/AppConfiguration.java
@@ -10,10 +10,10 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.HttpStatus;
 
-import javax.servlet.DispatcherType;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.SessionTrackingMode;
+import jakarta.servlet.DispatcherType;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.SessionTrackingMode;
 import java.util.EnumSet;
 
 @Configuration
diff --git a/tapestry-beanvalidator/build.gradle b/tapestry-beanvalidator/build.gradle
index 4e510a5ed..47f4aeefb 100644
--- a/tapestry-beanvalidator/build.gradle
+++ b/tapestry-beanvalidator/build.gradle
@@ -5,17 +5,16 @@ description = "Support for JSR-303 Bean Validation via the Hibernate validator i
 dependencies {
   implementation project(':tapestry-core')
 
-  implementation "javax.validation:validation-api:1.0.0.GA"
-  implementation "org.hibernate:hibernate-validator:4.3.2.Final"
+  implementation "jakarta.validation:jakarta.validation-api:3.0.2"
+  implementation "org.hibernate:hibernate-validator:8.0.1.Final"
 
   testImplementation project(':tapestry-test')
   implementation "org.seleniumhq.selenium:selenium-leg-rc:${versions.selenium}", {
       exclude group: "org.seleniumhq.selenium", module: "jetty-repacked"
       exclude group: "org.testng", module: "testng"
-      exclude group: "javax.servlet", module: "javax.servlet-api"
   }
   testImplementation "org.junit.jupiter:junit-jupiter:${versions.junitJupiter}"
-  
+
 }
 
 task compileCoffeeScript(type: CompileCoffeeScript) {
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorConfigurer.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorConfigurer.java
index 505ba7000..e9f25d06a 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorConfigurer.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorConfigurer.java
@@ -13,11 +13,11 @@
 // limitations under the License.
 package org.apache.tapestry5.beanvalidator;
 
-import javax.validation.Configuration;
+import jakarta.validation.Configuration;
 
 /**
  * Defines the interface for a chain-of-command that updates JSR-303 configuration in some way before the {@link
- * javax.validation.ValidatorFactory} is created.
+ * jakarta.validation.ValidatorFactory} is created.
  * 
  * @since 5.2.0.0
  */
@@ -27,4 +27,4 @@ public interface BeanValidatorConfigurer
 	 * Passed the configuration so as to make changes.
 	 */
 	void configure(Configuration<?> configuration);
-}
\ No newline at end of file
+}
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorSource.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorSource.java
index d65bf0ad2..62a135b46 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorSource.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorSource.java
@@ -13,13 +13,13 @@
 // limitations under the License.
 package org.apache.tapestry5.beanvalidator;
 
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
 
 import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
 
 /**
- * Creates {@link javax.validation.ValidatorFactory}.
+ * Creates {@link jakarta.validation.ValidatorFactory}.
  * 
  * @since 5.2.0.0
  */
@@ -36,4 +36,4 @@ public interface BeanValidatorSource
 	 * Returns the ValidatorFactory from which Validators are created.
 	 */
 	ValidatorFactory getValidatorFactory();
-}
\ No newline at end of file
+}
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java
index 5d7f1fb46..4a2f442fe 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java
@@ -30,7 +30,7 @@ public interface ClientConstraintDescriptor
     Class getAnnotationClass();
 
     /**
-     * Names of attributes from the {@link javax.validation.metadata.ConstraintDescriptor} that are relevant.
+     * Names of attributes from the {@link jakarta.validation.metadata.ConstraintDescriptor} that are relevant.
      */
     Set<String> getAttributes();
 
@@ -42,7 +42,7 @@ public interface ClientConstraintDescriptor
      * @param message
      *         error message to present to user when the constraint is violated
      * @param attributes
-     *         {@linkplain #getAttributes()} selected attributes} from the {@link javax.validation.metadata.ConstraintDescriptor}
+     *         {@linkplain #getAttributes()} selected attributes} from the {@link jakarta.validation.metadata.ConstraintDescriptor}
      */
     void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes);
 }
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java
index 6ab27d5c1..2f93c789b 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java
@@ -27,11 +27,11 @@ import org.apache.tapestry5.services.FieldValidatorDefaultSource;
 import org.apache.tapestry5.services.javascript.DataConstants;
 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
 
-import javax.validation.MessageInterpolator;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
-import javax.validation.constraints.*;
-import javax.validation.groups.Default;
+import jakarta.validation.MessageInterpolator;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
+import jakarta.validation.constraints.*;
+import jakarta.validation.groups.Default;
 import java.util.Map;
 
 /**
@@ -82,7 +82,7 @@ public class BeanValidatorModule
         configuration.add("LocaleAwareMessageInterpolator", new BeanValidatorConfigurer()
         {
             @Override
-            public void configure(javax.validation.Configuration<?> configuration)
+            public void configure(jakarta.validation.Configuration<?> configuration)
             {
                 MessageInterpolator defaultInterpolator = configuration.getDefaultMessageInterpolator();
 
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
index d8e7e5d47..6d1751f53 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
@@ -25,14 +25,14 @@ import org.apache.tapestry5.internal.BeanValidationContext;
 import org.apache.tapestry5.services.Environment;
 import org.apache.tapestry5.services.FormSupport;
 
-import javax.validation.ConstraintViolation;
-import javax.validation.MessageInterpolator;
-import javax.validation.MessageInterpolator.Context;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
-import javax.validation.metadata.BeanDescriptor;
-import javax.validation.metadata.ConstraintDescriptor;
-import javax.validation.metadata.PropertyDescriptor;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.MessageInterpolator;
+import jakarta.validation.MessageInterpolator.Context;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
+import jakarta.validation.metadata.BeanDescriptor;
+import jakarta.validation.metadata.ConstraintDescriptor;
+import jakarta.validation.metadata.PropertyDescriptor;
 
 import java.lang.annotation.Annotation;
 import java.util.Iterator;
@@ -87,7 +87,7 @@ public class BeanFieldValidator implements FieldValidator
         final String currentProperty = beanValidationContext.getCurrentProperty();
 
         if (currentProperty == null) return;
-        
+
         final ValidationInfo validationInfo = getValidationInfo(beanValidationContext, currentProperty, validator);
         final PropertyDescriptor propertyDescriptor = validationInfo.getPropertyDescriptor();
 
@@ -142,7 +142,7 @@ public class BeanFieldValidator implements FieldValidator
         String currentProperty = beanValidationContext.getCurrentProperty();
 
         if (currentProperty == null) return;
-        
+
         final ValidationInfo validationInfo = getValidationInfo(beanValidationContext, currentProperty, validator);
         final PropertyDescriptor propertyDescriptor = validationInfo.getPropertyDescriptor();
 
@@ -181,9 +181,9 @@ public class BeanFieldValidator implements FieldValidator
     final private static Class<?> getConstrainedPropertyClass(BeanDescriptor beanDescriptor, String propertyName)
     {
         Class<?> clasz = null;
-        for (PropertyDescriptor descriptor : beanDescriptor.getConstrainedProperties()) 
+        for (PropertyDescriptor descriptor : beanDescriptor.getConstrainedProperties())
         {
-            if (descriptor.getPropertyName().equals(propertyName)) 
+            if (descriptor.getPropertyName().equals(propertyName))
             {
                 clasz = descriptor.getElementClass();
                 break;
@@ -212,15 +212,21 @@ public class BeanFieldValidator implements FieldValidator
             {
                 return null;
             }
+
+            @Override
+            public <T> T unwrap(Class<T> type) {
+                if (type.isAssignableFrom(getClass())) return type.cast(this);
+                throw new jakarta.validation.ValidationException("Type " + type + " not supported for unwrapping.");
+            }
         });
     }
-    
+
     final private static ValidationInfo getValidationInfo(BeanValidationContext beanValidationContext, String currentProperty, Validator validator) {
         Class<?> beanType = beanValidationContext.getBeanType();
         String[] path = currentProperty.split("\\.");
         BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanType);
-        
-        for (int i = 1; i < path.length - 1; i++) 
+
+        for (int i = 1; i < path.length - 1; i++)
         {
             Class<?> constrainedPropertyClass = getConstrainedPropertyClass(beanDescriptor, path[i]);
             if (constrainedPropertyClass != null) {
@@ -233,35 +239,35 @@ public class BeanFieldValidator implements FieldValidator
         PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(propertyName);
         return new ValidationInfo(beanType, propertyName, propertyDescriptor);
     }
-    
+
     final private static class ValidationInfo {
         final private Class<?> beanType;
         final private String propertyName;
         final private PropertyDescriptor propertyDescriptor;
         public ValidationInfo(Class<?> beanType, String propertyName,
-                PropertyDescriptor propertyDescriptor) 
+                PropertyDescriptor propertyDescriptor)
         {
             super();
             this.beanType = beanType;
             this.propertyName = propertyName;
             this.propertyDescriptor = propertyDescriptor;
         }
-        
-        public Class<?> getBeanType() 
+
+        public Class<?> getBeanType()
         {
             return beanType;
         }
-        
-        public String getPropertyName() 
+
+        public String getPropertyName()
         {
             return propertyName;
         }
 
-        public PropertyDescriptor getPropertyDescriptor() 
+        public PropertyDescriptor getPropertyDescriptor()
         {
             return propertyDescriptor;
         }
 
     }
-    
+
 }
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidatorDefaultSource.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidatorDefaultSource.java
index 4f71f7ff6..8220d8c11 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidatorDefaultSource.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidatorDefaultSource.java
@@ -16,7 +16,7 @@ package org.apache.tapestry5.internal.beanvalidator;
 import java.util.Arrays;
 import java.util.Locale;
 
-import javax.validation.ValidatorFactory;
+import jakarta.validation.ValidatorFactory;
 
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.Field;
@@ -89,4 +89,4 @@ public class BeanFieldValidatorDefaultSource implements FieldValidatorDefaultSou
 				.getContainerMessages(), resources.getLocale(), propertyType,
 				resources.getAnnotationProvider(parameterName));
 	}
-}
\ No newline at end of file
+}
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanValidatorSourceImpl.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanValidatorSourceImpl.java
index 3e5804e16..ef564e2eb 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanValidatorSourceImpl.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanValidatorSourceImpl.java
@@ -15,10 +15,10 @@ package org.apache.tapestry5.internal.beanvalidator;
 
 import java.util.List;
 
-import javax.validation.Configuration;
-import javax.validation.Validation;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
+import jakarta.validation.Configuration;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
 
 import org.apache.tapestry5.beanvalidator.BeanValidatorConfigurer;
 import org.apache.tapestry5.beanvalidator.BeanValidatorSource;
@@ -51,4 +51,4 @@ public class BeanValidatorSourceImpl implements BeanValidatorSource
 		return this.validatorFactory;
 	}
 
-}
\ No newline at end of file
+}
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/MessageInterpolatorImpl.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/MessageInterpolatorImpl.java
index dadeb5bb6..58c0e573a 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/MessageInterpolatorImpl.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/MessageInterpolatorImpl.java
@@ -15,7 +15,7 @@ package org.apache.tapestry5.internal.beanvalidator;
 
 import java.util.Locale;
 
-import javax.validation.MessageInterpolator;
+import jakarta.validation.MessageInterpolator;
 
 import org.apache.tapestry5.ioc.services.ThreadLocale;
 /**
@@ -34,7 +34,7 @@ public class MessageInterpolatorImpl implements MessageInterpolator
 	}
 
 	/**
-	 * @see javax.validation.MessageInterpolator#interpolate(java.lang.String, javax.validation.MessageInterpolator.Context)
+	 * @see jakarta.validation.MessageInterpolator#interpolate(java.lang.String, jakarta.validation.MessageInterpolator.Context)
 	 */
 	@Override
 	public String interpolate(String messageTemplate, Context context) 
@@ -43,7 +43,7 @@ public class MessageInterpolatorImpl implements MessageInterpolator
 	}
 
 	/**
-	 * @see javax.validation.MessageInterpolator#interpolate(java.lang.String, javax.validation.MessageInterpolator.Context, java.util.Locale)
+	 * @see jakarta.validation.MessageInterpolator#interpolate(java.lang.String, jakarta.validation.MessageInterpolator.Context, java.util.Locale)
 	 */
 	@Override
 	public String interpolate(String messageTemplate, Context context, Locale locale) 
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/BeanForTAP1981.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/BeanForTAP1981.java
index 727460482..7f44a37d8 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/BeanForTAP1981.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/BeanForTAP1981.java
@@ -1,6 +1,6 @@
 package org.example.testapp.entities;
 
-import javax.validation.constraints.Max;
+import jakarta.validation.constraints.Max;
 
 public class BeanForTAP1981 {
 
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java
index 44af90da4..2d1cc8b32 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/ComplexBean.java
@@ -1,7 +1,7 @@
 package org.example.testapp.entities;
 
-import javax.validation.Valid;
-import javax.validation.constraints.NotNull;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
 
 public class ComplexBean
 {
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeOtherSimpleBean.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeOtherSimpleBean.java
index 9a262f33f..2d6445968 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeOtherSimpleBean.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeOtherSimpleBean.java
@@ -1,6 +1,6 @@
 package org.example.testapp.entities;
 
-import javax.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotNull;
 
 public class SomeOtherSimpleBean
 {
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeSimpleBean.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeSimpleBean.java
index 156411287..9306566ba 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeSimpleBean.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/SomeSimpleBean.java
@@ -1,6 +1,6 @@
 package org.example.testapp.entities;
 
-import javax.validation.constraints.Min;
+import jakarta.validation.constraints.Min;
 
 public class SomeSimpleBean
 {
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java
index b296b954d..a80679564 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/TestEntity.java
@@ -16,7 +16,7 @@ package org.example.testapp.entities;
 import java.util.ArrayList;
 import java.util.Collection;
 
-import javax.validation.constraints.*;
+import jakarta.validation.constraints.*;
 
 public class TestEntity
 {
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/User.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/User.java
index c8c6239c0..80a934f52 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/User.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/entities/User.java
@@ -13,9 +13,9 @@
 // limitations under the License.
 package org.example.testapp.entities;
 
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
 
 public class User
 {
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java
index a5ed4ff88..6f36c77c7 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java
@@ -17,9 +17,9 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
 
 import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
index 89a6e9479..11defed2d 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
@@ -17,7 +17,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 
-import javax.validation.constraints.*;
+import jakarta.validation.constraints.*;
 
 import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java
index 980852335..75ae1536c 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java
@@ -15,9 +15,9 @@ package org.example.testapp.pages;
 
 import java.util.Set;
 
-import javax.validation.ConstraintViolation;
-import javax.validation.Validator;
-import javax.validation.constraints.NotNull;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validator;
+import jakarta.validation.constraints.NotNull;
 
 import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.InjectComponent;
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java
index 3da7bf767..6ad7f71ed 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/services/AppModule.java
@@ -38,7 +38,7 @@ public class AppModule
         {
 
             @Override
-            public void configure(javax.validation.Configuration<?> configuration)
+            public void configure(jakarta.validation.Configuration<?> configuration)
             {
                 configuration.ignoreXmlConfiguration();
             }
diff --git a/tapestry-beanvalidator/src/test/resources/ValidationMessages_en.properties b/tapestry-beanvalidator/src/test/resources/ValidationMessages_en.properties
index 10539ea0c..75e7c0590 100644
--- a/tapestry-beanvalidator/src/test/resources/ValidationMessages_en.properties
+++ b/tapestry-beanvalidator/src/test/resources/ValidationMessages_en.properties
@@ -1,17 +1,17 @@
-javax.validation.constraints.AssertFalse.message=must be false
-javax.validation.constraints.AssertTrue.message=must be true
-javax.validation.constraints.DecimalMax.message=must be less than or equal to {value}
-javax.validation.constraints.DecimalMin.message=must be greater than or equal to {value}
-javax.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
-javax.validation.constraints.Future.message=must be in the future
-javax.validation.constraints.Max.message=must be less than or equal to {value}
-javax.validation.constraints.Min.message=must be greater than or equal to {value}
-javax.validation.constraints.NotNull.message=may not be null
-javax.validation.constraints.Null.message=must be null
-javax.validation.constraints.Past.message=must be in the past
-javax.validation.constraints.Pattern.message=must match "{regexp}"
-javax.validation.constraints.Size.message=size must be between {min} and {max}
+jakarta.validation.constraints.AssertFalse.message=must be false
+jakarta.validation.constraints.AssertTrue.message=must be true
+jakarta.validation.constraints.DecimalMax.message=must be less than or equal to {value}
+jakarta.validation.constraints.DecimalMin.message=must be greater than or equal to {value}
+jakarta.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
+jakarta.validation.constraints.Future.message=must be in the future
+jakarta.validation.constraints.Max.message=must be less than or equal to {value}
+jakarta.validation.constraints.Min.message=must be greater than or equal to {value}
+jakarta.validation.constraints.NotNull.message=may not be null
+jakarta.validation.constraints.Null.message=must be null
+jakarta.validation.constraints.Past.message=must be in the past
+jakarta.validation.constraints.Pattern.message=must match "{regexp}"
+jakarta.validation.constraints.Size.message=size must be between {min} and {max}
 org.hibernate.validator.constraints.Email.message="{value}" is not a valid email address
 org.hibernate.validator.constraints.Length.message=length must be between {min} and {max}
 org.hibernate.validator.constraints.NotEmpty.message=may not be empty
-org.hibernate.validator.constraints.Range.message={value} must be between {min} and {max}
\ No newline at end of file
+org.hibernate.validator.constraints.Range.message={value} must be between {min} and {max}
diff --git a/tapestry-clojure/build.gradle b/tapestry-clojure/build.gradle
index d1f044446..95cacd151 100644
--- a/tapestry-clojure/build.gradle
+++ b/tapestry-clojure/build.gradle
@@ -6,7 +6,7 @@ dependencies {
 
   // Added just to prove that it works (TAP5-1945)
   testImplementation project(':tapestry-core')
-  testRuntimeOnly "javax.servlet:javax.servlet-api:${versions.servletapi}"
+  testRuntimeOnly "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
   testImplementation "org.junit.jupiter:junit-jupiter:${versions.junitJupiter}"
 }
 
@@ -18,4 +18,4 @@ jar {
   manifest {
     attributes 'Tapestry-Module-Classes': 'org.apache.tapestry5.clojure.modules.ClojureModule'
   }
-}
\ No newline at end of file
+}
diff --git a/tapestry-core/build.gradle b/tapestry-core/build.gradle
index 142197c01..e999a6586 100644
--- a/tapestry-core/build.gradle
+++ b/tapestry-core/build.gradle
@@ -19,7 +19,7 @@ dependencies {
 
     api "org.apache.commons:commons-lang3:${versions.commonsLang}"
 
-    
+
     implementation 'jakarta.annotation:jakarta.annotation-api:1.3.4'
     implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.2'
     implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.2'
@@ -29,10 +29,10 @@ dependencies {
     provided project(":tapestry-test")
     provided project(":tapestry-test-constants")
 
-    provided "javax.servlet:javax.servlet-api:${versions.servletapi}"
+    provided "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
+
+    implementation "commons-codec:commons-codec:1.13"
 
-    implementation "commons-codec:commons-codec:1.10"
-    
     testImplementation "org.junit.jupiter:junit-jupiter:${versions.junitJupiter}"
     testImplementation "org.apache.httpcomponents:httpclient:4.5.14"
     testRuntimeOnly project(':tapestry-spock')
@@ -126,4 +126,4 @@ task runTestAppfolder(type:JavaExec) {
 
 task testWithPrototype(type:Test) {
   systemProperties."tapestry.javascript-infrastructure-provider" = "prototype"
-}
\ No newline at end of file
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
index 07e4d28de..c4874b783 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
@@ -58,7 +58,7 @@ public class SymbolConstants
      * reported, whether far-future expire headers are emitted, whether JavaScript files may be combined,
      * whether JSON is compressed, whether component field and parameter values are shadowed to instance
      * variables (to assist with debugging), and more.
-     * 
+     *
      * This is an alias to {@link TapestryHttpSymbolConstants#PRODUCTION_MODE}.
      */
     public static final String PRODUCTION_MODE = TapestryHttpSymbolConstants.PRODUCTION_MODE;
@@ -157,7 +157,7 @@ public class SymbolConstants
      * for documentation purposes; it appears in the default exception report page, for example.
      *
      * The default value is "0.0.1".  In 5.3 and earlier, the default value was a random hexadecimal string.
-     * 
+     *
      * This is an alias to {@link TapestryHttpSymbolConstants#APPLICATION_VERSION}.
      *
      * @see AssetDispatcher
@@ -235,8 +235,8 @@ public class SymbolConstants
     /**
      * The default stylesheet automatically inserted into every rendered HTML page when
      * no Bootstrap version is enabled (i.e both {@link Trait#BOOTSTRAP_3} and {@link Trait#BOOTSTRAP_4}
-     * traits are disabled, something done by {@linkplain NoBootstrapModule}). 
-     * 
+     * traits are disabled, something done by {@linkplain NoBootstrapModule}).
+     *
      * It was deprecated in 5.4 with no replacement (the stylesheet is now associated with the core {@link JavaScriptStack}.),
      * but undeprecated in 5.5.0 with the caveat described above.
      *
@@ -330,7 +330,7 @@ public class SymbolConstants
      *
      * The default is {@code true}, to preserve 5.2 behavior. For non-clustered applications (the majority), this value should be
      * overridden to {@code false}. A future release of Tapestry may change the default.
-     * 
+     *
      * This is an alias to {@link TapestryHttpSymbolConstants#CLUSTERED_SESSIONS}.
      *
      * @since 5.3
@@ -387,7 +387,7 @@ public class SymbolConstants
     public static final String MODULE_PATH_PREFIX = "tapestry.module-path-prefix";
 
     /**
-     * Identifies the context path of the application, as determined from {@link javax.servlet.ServletContext#getContextPath()}.
+     * Identifies the context path of the application, as determined from {@link jakarta.servlet.ServletContext#getContextPath()}.
      * This is either a blank string or a string that starts with a slash but does not end with one.
      * This is an alias to {@link TapestryHttpSymbolConstants#CONTEXT_PATH}.
      *
@@ -442,14 +442,14 @@ public class SymbolConstants
     public static final String JAVASCRIPT_INFRASTRUCTURE_PROVIDER = "tapestry.javascript-infrastructure-provider";
 
     /**
-     * If true (the default), then Tapestry will apply locking semantics around access to the {@link javax.servlet.http.HttpSession}.
+     * If true (the default), then Tapestry will apply locking semantics around access to the {@link jakarta.servlet.http.HttpSession}.
      * Reading attribute names occurs with a shared read lock; getting or setting an attribute upgrades to an exclusive write lock.
      * This can tend to serialize threads when a number of simultaneous (Ajax) requests from the client arrive ... however,
      * many implementations of HttpSession are not thread safe, and often mutable objects are stored in the session and shared
      * between threads. Leaving this on the default will yield a more robust application; setting it to false may speed
      * up processing for more Ajax intensive applications (but care should then be given to ensuring that objects shared inside
      * the session are themselves immutable or thread-safe).
-     * 
+     *
      * This is an alias to {@link TapestryHttpSymbolConstants#SESSION_LOCKING_ENABLED}.
      *
      * @since 5.4
@@ -469,7 +469,7 @@ public class SymbolConstants
      * Defines the CSS class that will be given to HTML element (usually a div) &lt;div&gt; generated by
      * the {@linkplain FormGroup} mixin and the
      * {@linkplain BeanEditForm} and {@linkplain BeanEditor}
-     * components surrounding the label and the field. If the value isn't 
+     * components surrounding the label and the field. If the value isn't
      * {@code form-group}, the div will have {@code class="form-group [value]}.
      * The default value is <code>form-group</code>.
      *
@@ -555,7 +555,7 @@ public class SymbolConstants
      * @since 5.5
      */
     public static final String AJAX_FORM_LOOP_ADD_ROW_LINK_CSS_CLASS = "tapestry.ajax-form-loop-add-row-link-css-class";
-    
+
     /**
      * Defines the prefix of the CSS class that will be given to the outer &lt;div&gt; element generated by
      * {@linkplain Errors}. The value of {@linkplain Errors}'s <code>class</code> parameter appended
@@ -653,7 +653,7 @@ public class SymbolConstants
      * @since 5.4
      */
     public static final String PRELOADER_MODE = "tapestry.page-preload-mode";
-    
+
     /**
      * Defines the OpenAPI version to be used in the generated OpenAPI description.
      * Default value is <code>3.0.0</code>.
@@ -662,7 +662,7 @@ public class SymbolConstants
      * @since 5.8.0
      */
     public static final String OPENAPI_VERSION = "tapestry.openapi-version";
-    
+
     /**
      * Defines the title of this application in the generated OpenAPI description. No default value is provided.
      * @see DefaultOpenApiDescriptionGenerator
@@ -672,7 +672,7 @@ public class SymbolConstants
     public static final String OPENAPI_TITLE = "tapestry.openapi-title";
 
     /**
-     * Defines the description of this application in the generated OpenAPI description. 
+     * Defines the description of this application in the generated OpenAPI description.
      * No default value is provided.
      * @see DefaultOpenApiDescriptionGenerator
      * @see OpenApiDescriptionGenerator
@@ -681,7 +681,7 @@ public class SymbolConstants
     public static final String OPENAPI_DESCRIPTION = "tapestry.openapi-description";
 
     /**
-     * Defines the version of this application in the generated OpenAPI description (i.e. info/version). 
+     * Defines the version of this application in the generated OpenAPI description (i.e. info/version).
      * No default value is provided.
      * @see DefaultOpenApiDescriptionGenerator
      * @see OpenApiDescriptionGenerator
@@ -690,7 +690,7 @@ public class SymbolConstants
     public static final String OPENAPI_APPLICATION_VERSION = "tapestry.openapi-application-version";
 
     /**
-     * Defines whether the OpenAPI description file of this application's REST endpoints should be 
+     * Defines whether the OpenAPI description file of this application's REST endpoints should be
      * published or not. The default value is <code>false</code>.
      * @see OpenApiDescriptionGenerator
      * @see #OPENAPI_DESCRIPTION_PATH
@@ -707,11 +707,11 @@ public class SymbolConstants
      * @since 5.8.0
      */
     public static final String OPENAPI_DESCRIPTION_PATH = "tapestry.openapi-description-path";
-    
+
     /**
      * Defines a base path to the generated OpenAPI description relative to the application
      * URL as defined by {@link BaseURLSource#getBaseURL(boolean)}. It should be either
-     * the empty string, meaning there's no base path, or a string starting and ending 
+     * the empty string, meaning there's no base path, or a string starting and ending
      * with a slash. Default value is "/" (without the quotes)
      * @see OpenApiDescriptionGenerator
      * @since 5.8.0
@@ -719,7 +719,7 @@ public class SymbolConstants
     public static final String OPENAPI_BASE_PATH = "tapestry.openapi-base-path";
 
     /**
-     * Defines whether the CORS (Cross-Origing Resource Sharing) support 
+     * Defines whether the CORS (Cross-Origing Resource Sharing) support
      * should be enabled or not. Default value is "false". If you set this to "true",
      * you should also set {@link #CORS_ALLOWED_ORIGINS}.
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_ENABLED}.
@@ -728,20 +728,20 @@ public class SymbolConstants
      * @since 5.8.2
      */
     public static final String CORS_ENABLED = TapestryHttpSymbolConstants.CORS_ENABLED;
-    
+
     /**
      * Comma-delimited of origins allowed for CORS. The special value "*" means allowing all origins.
-     * This is used by the default implementation of {@link CorsHandlerHelper#getAllowedOrigin(javax.servlet.http.HttpServletRequest)}.
+     * This is used by the default implementation of {@link CorsHandlerHelper#getAllowedOrigin(jakarta.servlet.http.HttpServletRequest)}.
      * Default value is the empty string (i.e. no origins allowed and CORS actually disabled).
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_ALLOWED_ORIGINS}.
      * @since 5.8.2
      */
     public static final String CORS_ALLOWED_ORIGINS = TapestryHttpSymbolConstants.CORS_ALLOWED_ORIGINS;
-    
+
     /**
      * Boolean value defining whether the Access-Control-Allow-Credentials HTTP header
      * should be set automatically in the response for CORS requests. Default value is
-     * <code>false</code>. This is used by the default implementation of {@link CorsHandlerHelper#configureCredentials(javax.servlet.http.HttpServletResponse)}.
+     * <code>false</code>. This is used by the default implementation of {@link CorsHandlerHelper#configureCredentials(jakarta.servlet.http.HttpServletResponse)}.
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_ALLOW_CREDENTIALS}.
      * @since 5.8.2
      */
@@ -749,7 +749,7 @@ public class SymbolConstants
 
     /**
      * Value to be used in the Access-Control-Allow-Methods in CORS preflight request responses.
-     * This is used by the default implementation of {@link CorsHandlerHelper#configureMethods(javax.servlet.http.HttpServletResponse)}.
+     * This is used by the default implementation of {@link CorsHandlerHelper#configureMethods(jakarta.servlet.http.HttpServletResponse)}.
      * Default value is <code>GET,HEAD,PUT,PATCH,POST,DELETE</code>.
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_ALLOW_METHODS}.
      * @since 5.8.2
@@ -758,16 +758,16 @@ public class SymbolConstants
 
     /**
      * Value to be used in the Access-Control-Allow-Headers in CORS preflight request responses.
-     * This is used by the default implementation of {@link CorsHandlerHelper#configureAllowedHeaders(javax.servlet.http.HttpServletResponse, javax.servlet.http.HttpServletRequest)}.
+     * This is used by the default implementation of {@link CorsHandlerHelper#configureAllowedHeaders(jakarta.servlet.http.HttpServletResponse, jakarta.servlet.http.HttpServletRequest)}.
      * Default value is the empty string.
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_ALLOWED_HEADERS}.
      * @since 5.8.2
      */
     public static final String CORS_ALLOWED_HEADERS = TapestryHttpSymbolConstants.CORS_ALLOWED_HEADERS;
-    
+
     /**
      * Value to be used in the Access-Control-Expose-Headers in CORS request responses.
-     * This is used by the default implementation of {@link CorsHandlerHelper#configureExposeHeaders(javax.servlet.http.HttpServletResponse)}.
+     * This is used by the default implementation of {@link CorsHandlerHelper#configureExposeHeaders(jakarta.servlet.http.HttpServletResponse)}.
      * Default value is the empty string.
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_EXPOSE_HEADERS}.
      * @since 5.8.2
@@ -776,13 +776,13 @@ public class SymbolConstants
 
     /**
      * Value to be used in the Access-Control-Max-Age in responses to preflight CORS requests.
-     * This is used by the default implementation of {@link CorsHandlerHelper#configureMaxAge(javax.servlet.http.HttpServletResponse)}.
+     * This is used by the default implementation of {@link CorsHandlerHelper#configureMaxAge(jakarta.servlet.http.HttpServletResponse)}.
      * Default value is the empty string.
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_MAX_AGE}.
      * @since 5.8.2
      */
     public static final String CORS_MAX_AGE = TapestryHttpSymbolConstants.CORS_MAX_AGE;
-    
+
     /**
      * Defines whether multiple classloaders will be used instead of one for smarter page invalidation.
      * This is ignored when in production mode.
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java
index 2795a03e1..ee30399e1 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryFilter.java
@@ -12,7 +12,7 @@
 
 package org.apache.tapestry5;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 import org.apache.tapestry5.modules.TapestryModule;
 
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java
index beead7ba1..69313c37b 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java
@@ -174,8 +174,8 @@ public class Palette extends AbstractField
         deprecationWarning.ignoredComponentParameters(resources, "select", "moveUp", "moveDown", "deselect");
     }
 
-
-    public final Renderable mainRenderer = new Renderable()
+    @Property(write = false)
+    private final Renderable mainRenderer = new Renderable()
     {
         public void render(MarkupWriter writer)
         {
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java
index 12836755d..925d9807c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetDispatcher.java
@@ -28,7 +28,7 @@ import org.apache.tapestry5.services.ClasspathAssetAliasManager;
 import org.apache.tapestry5.services.PathConstructor;
 import org.apache.tapestry5.services.assets.AssetRequestHandler;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Comparator;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSink.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSink.java
index ba855cc60..58588d7e9 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSink.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSink.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.internal.services;
 
-import javax.servlet.http.Cookie;
+import jakarta.servlet.http.Cookie;
 
 /**
  * You can write cookies to it.
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSource.java
index dab31d045..6fbac60d2 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookieSource.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.internal.services;
 
-import javax.servlet.http.Cookie;
+import jakarta.servlet.http.Cookie;
 
 /**
  * Provides the cookies from the browser.
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java
index cccda7de5..78ba79747 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/CookiesImpl.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.internal.services;
 
-import javax.servlet.http.Cookie;
+import jakarta.servlet.http.Cookie;
 
 import org.apache.tapestry5.CookieBuilder;
 import org.apache.tapestry5.SymbolConstants;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultRequestExceptionHandler.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultRequestExceptionHandler.java
index 497b94bd4..97b918e83 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultRequestExceptionHandler.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultRequestExceptionHandler.java
@@ -33,7 +33,7 @@ import org.apache.tapestry5.services.ExceptionReporter;
 import org.apache.tapestry5.services.RequestExceptionHandler;
 import org.slf4j.Logger;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.URLEncoder;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/IgnoredPathsFilter.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/IgnoredPathsFilter.java
index af33367cd..9d980858f 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/IgnoredPathsFilter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/IgnoredPathsFilter.java
@@ -17,8 +17,8 @@ package org.apache.tapestry5.internal.services;
 import org.apache.tapestry5.http.services.HttpServletRequestFilter;
 import org.apache.tapestry5.http.services.HttpServletRequestHandler;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.regex.Pattern;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
index a284565fa..a9d3fc355 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamer.java
@@ -18,7 +18,7 @@ import org.apache.tapestry5.commons.Resource;
 import org.apache.tapestry5.services.assets.StreamableResource;
 import org.apache.tapestry5.services.assets.StreamableResourceSource;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.EnumSet;
 import java.util.Set;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
index 363c0b71c..92923b189 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java
@@ -28,7 +28,7 @@ import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.services.AssetFactory;
 import org.apache.tapestry5.services.assets.*;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Set;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaticFilesFilter.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaticFilesFilter.java
index 7001db747..316d89df7 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaticFilesFilter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaticFilesFilter.java
@@ -17,7 +17,7 @@ package org.apache.tapestry5.internal.services;
 import java.io.IOException;
 import java.net.URL;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.TapestryConstants;
 import org.apache.tapestry5.http.services.Context;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UnknownActivationContextHandlerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UnknownActivationContextHandlerImpl.java
index 70a25541b..6b00ceb54 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UnknownActivationContextHandlerImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UnknownActivationContextHandlerImpl.java
@@ -22,7 +22,7 @@ import org.apache.tapestry5.services.HttpError;
 import org.apache.tapestry5.services.Traditional;
 import org.slf4j.Logger;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
 /**
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java
index 30522b4de..5d9ba3e1e 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/ModuleDispatcher.java
@@ -29,7 +29,7 @@ import org.apache.tapestry5.services.PathConstructor;
 import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
 import org.apache.tapestry5.services.javascript.ModuleManager;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.EnumSet;
 import java.util.List;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java
index d0bd12f66..9e2c43f92 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/rest/DefaultOpenApiDescriptionGenerator.java
@@ -27,7 +27,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.annotations.ActivationContextParameter;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableCookieSinkSource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableCookieSinkSource.java
index d61668e64..8f3cb0fde 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableCookieSinkSource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableCookieSinkSource.java
@@ -18,7 +18,7 @@ import org.apache.tapestry5.commons.util.CollectionFactory;
 import org.apache.tapestry5.internal.services.CookieSink;
 import org.apache.tapestry5.internal.services.CookieSource;
 
-import javax.servlet.http.Cookie;
+import jakarta.servlet.http.Cookie;
 import java.util.Map;
 
 public class TestableCookieSinkSource implements CookieSource, CookieSink
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java
index 2e75ba345..11016ba63 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java
@@ -151,6 +151,7 @@ public class TestableRequestImpl implements TestableRequest
         return path;
     }
 
+    @Override
     public String getContextPath()
     {
         return contextPath;
@@ -245,7 +246,7 @@ public class TestableRequestImpl implements TestableRequest
 
     /**
      * Always returns 80.
-     * 
+     *
      * @since 5.2.5
      */
     public int getServerPort()
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java
index 532e0e135..a5787dff9 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java
@@ -23,8 +23,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.WriteListener;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.commons.util.CollectionFactory;
 import org.apache.tapestry5.dom.Document;
@@ -47,7 +48,7 @@ public class TestableResponseImpl implements TestableResponse
     private String errorMessage;
 
     private int contentLength = 0;
-    
+
     private String contentType;
 
     private final ByteArrayOutputStream output = new ByteArrayOutputStream();
@@ -64,14 +65,14 @@ public class TestableResponseImpl implements TestableResponse
     public OutputStream getOutputStream(String contentType) throws IOException
     {
         this.contentType = contentType;
-        
+
         return this.outputStream;
     }
 
     public PrintWriter getPrintWriter(String contentType) throws IOException
     {
         committed = true;
-        
+
         this.contentType = contentType;
 
         if (printWriter == null)
@@ -111,7 +112,7 @@ public class TestableResponseImpl implements TestableResponse
     {
         headers.put(name, value);
     }
-    
+
     @SuppressWarnings("unchecked")
     public void addHeader(String name, String value)
     {
@@ -122,7 +123,7 @@ public class TestableResponseImpl implements TestableResponse
         }
         values.add(value);
     }
-    
+
     public List<?> getHeaders(String name)
     {
         return (List<?>) headers.get(name);
@@ -223,7 +224,7 @@ public class TestableResponseImpl implements TestableResponse
     {
         this.committed = true;
     }
-    
+
     public String getContentType()
     {
         return this.contentType;
@@ -268,5 +269,15 @@ public class TestableResponseImpl implements TestableResponse
             this.delegate.close();
         }
 
+        @Override
+        public boolean isReady()
+        {
+            return true;
+        }
+
+        @Override
+        public void setWriteListener(WriteListener writeListener)
+        {
+        }
     }
 }
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/InternalModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/InternalModule.java
index 122068aba..88e92a545 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/InternalModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/InternalModule.java
@@ -80,7 +80,7 @@ import org.apache.tapestry5.services.LinkCreationListener2;
 import org.apache.tapestry5.services.LocalizationSetter;
 import org.apache.tapestry5.services.transform.ControlledPackageType;
 
-import javax.servlet.http.Cookie;
+import jakarta.servlet.http.Cookie;
 import java.util.Map;
 
 /**
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/DelegatingRequest.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/DelegatingRequest.java
index db4c2b008..aeb1c5ea0 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/DelegatingRequest.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/DelegatingRequest.java
@@ -22,7 +22,7 @@ import org.apache.tapestry5.http.services.Session;
 
 /**
  * Class that wraps an {@linkplain Request}, delegating all its methods.
- * 
+ *
  * @since 5.1.0.1
  */
 public class DelegatingRequest implements Request
@@ -39,7 +39,7 @@ public class DelegatingRequest implements Request
 
     /**
      * Constructor that receives a {@linkplain Request}.
-     * 
+     *
      * @param request
      *            a {@link Request}. It cannot be null.
      */
@@ -50,7 +50,7 @@ public class DelegatingRequest implements Request
 
     /**
      * Sets the delegate request.
-     * 
+     *
      * @param request
      *            a {@link Request}. It cannot be null.
      */
@@ -70,6 +70,7 @@ public class DelegatingRequest implements Request
         return request.getAttributeNames();
     }
 
+    @Override
     public String getContextPath()
     {
         return request.getContextPath();
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/HttpStatus.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/HttpStatus.java
index 30eb2ee72..d94f3f8a2 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/HttpStatus.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/HttpStatus.java
@@ -19,7 +19,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.StreamResponse;
 import org.apache.tapestry5.http.Link;
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetRequestHandler.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetRequestHandler.java
index 379f96086..cf93b740a 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetRequestHandler.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/assets/AssetRequestHandler.java
@@ -17,7 +17,7 @@ import org.apache.tapestry5.http.services.Request;
 import org.apache.tapestry5.http.services.Response;
 import org.apache.tapestry5.internal.services.AssetDispatcher;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
 /**
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java b/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java
index cb53579c5..31bd02e68 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/test/TapestryTestCase.java
@@ -71,10 +71,10 @@ import org.apache.tapestry5.services.ValueEncoderSource;
 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
 import org.easymock.IAnswer;
 
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app5/pages/Error404.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app5/pages/Error404.groovy
index d4cbe40f7..ed8970f3a 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app5/pages/Error404.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app5/pages/Error404.groovy
@@ -1,6 +1,6 @@
 package org.apache.tapestry5.integration.app5.pages
 
-import javax.servlet.http.HttpServletResponse
+import jakarta.servlet.http.HttpServletResponse
 
 import org.apache.tapestry5.http.services.Request
 import org.apache.tapestry5.http.services.Response
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerTest.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerTest.groovy
index 530830f94..3703da33e 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerTest.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/ResponseCompressionAnalyzerTest.groovy
@@ -1,6 +1,6 @@
 package org.apache.tapestry5.internal.services
 
-import javax.servlet.http.HttpServletRequest
+import jakarta.servlet.http.HttpServletRequest
 
 import org.apache.tapestry5.http.internal.services.ResponseCompressionAnalyzerImpl
 import org.apache.tapestry5.http.services.ResponseCompressionAnalyzer
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/services/javascript/ModuleDispatcherTests.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/services/javascript/ModuleDispatcherTests.groovy
index 7f8eaf335..6dcb097a1 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/services/javascript/ModuleDispatcherTests.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/services/javascript/ModuleDispatcherTests.groovy
@@ -1,6 +1,6 @@
 package org.apache.tapestry5.services.javascript
 
-import javax.servlet.http.HttpServletResponse
+import jakarta.servlet.http.HttpServletResponse
 
 import org.apache.tapestry5.http.services.Request
 import org.apache.tapestry5.http.services.Response
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java
index 573fe8b3c..988f2f8ef 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java
@@ -11,7 +11,7 @@
 // limitations under the License.
 package org.apache.tapestry5.integration.app1.base;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.EventConstants;
 import org.apache.tapestry5.annotations.ActivationContextParameter;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormFragmentExplicitVisibleBoundsDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormFragmentExplicitVisibleBoundsDemo.java
index 5b7528b93..518711bbc 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormFragmentExplicitVisibleBoundsDemo.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/FormFragmentExplicitVisibleBoundsDemo.java
@@ -18,14 +18,13 @@ import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 
 import javax.inject.Inject;
-import javax.persistence.PersistenceProperty;
 
 /**
  * Demos the use of explicit bounds for checking visibility of a form fragment for form submission processing.
  * By default, a FormFragment searches to make sure the containing form is visible via "isDeepVisible".  If
  * no intermediate parent elements are invisible, the fragment is considered visible.  However, there are times when
  * that behavior is not desired; some element other than form should be used as the stopping point for determining
- * visibility.  This page demonstrates that use case.
+ * visibility.  This page demonstrates that use  case.
  */
 public class FormFragmentExplicitVisibleBoundsDemo {
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReturnTypes.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReturnTypes.java
index bdad70431..759114fc4 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReturnTypes.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ReturnTypes.java
@@ -23,7 +23,7 @@ import org.apache.tapestry5.services.PageRenderLinkSource;
 import org.apache.tapestry5.services.StreamPageContent;
 import org.apache.tapestry5.util.TextStreamResponse;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.net.MalformedURLException;
 import java.net.URL;
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app2/pages/TestPageForHttpError.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app2/pages/TestPageForHttpError.java
index 2ad892915..2a6b4118e 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app2/pages/TestPageForHttpError.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app2/pages/TestPageForHttpError.java
@@ -13,7 +13,7 @@
 // limitations under the License.
 package org.apache.tapestry5.integration.app2.pages;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.services.HttpError;
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java
index 3b6f5f697..7e52ff03a 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java
@@ -23,8 +23,9 @@ import io.github.bonigarcia.wdm.managers.FirefoxDriverManager;
 import org.apache.tapestry5.test.JettyRunner;
 import org.apache.tapestry5.test.TapestryTestConstants;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.session.JDBCSessionIdManager;
-import org.eclipse.jetty.server.session.JDBCSessionManager;
+import org.eclipse.jetty.server.session.DatabaseAdaptor;
+import org.eclipse.jetty.server.session.DefaultSessionIdManager;
+import org.eclipse.jetty.server.session.JDBCSessionDataStore;
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.webapp.WebAppContext;
 import org.openqa.selenium.firefox.FirefoxDriver;
@@ -196,23 +197,18 @@ public class ClusterTests
 
         runner.configure("src/test/cluster", "", port, port + 100);
 
-        JDBCSessionIdManager idMgr = new JDBCSessionIdManager(runner.getServer());
-        idMgr.setWorkerName(name);
-        idMgr.setDriverInfo("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:clustertest");
+        JDBCSessionDataStore jdbcSessionDataStore = new JDBCSessionDataStore();
+        DatabaseAdaptor databaseAdaptor = new DatabaseAdaptor();
+        databaseAdaptor.setDriverInfo("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:clustertest");
+        jdbcSessionDataStore.setDatabaseAdaptor(databaseAdaptor);
 
         Server server = runner.getServer();
-        server.setSessionIdManager(idMgr);
+        server.setSessionIdManager(new DefaultSessionIdManager(server));
 
         WebAppContext wac = (WebAppContext) server.getHandler();
 
-        JDBCSessionManager jdbcMgr = new JDBCSessionManager();
-        jdbcMgr.setIdManager(server.getSessionIdManager());
+        server.addBean(jdbcSessionDataStore);
 
-        // force the session to be read from the database with no delay
-        // This is an incorrectly documented feature.
-        jdbcMgr.setSaveInterval(0);
-
-        wac.setSessionHandler(new SessionHandler(jdbcMgr));
         wac.getServletContext().setInitParameter("cluster.name", name);
         runner.start();
         return runner;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/rest/RestTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/rest/RestTests.java
index 5e5a882c2..3ea5c333f 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/rest/RestTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/rest/RestTests.java
@@ -16,7 +16,7 @@ package org.apache.tapestry5.integration.rest;
 
 import java.io.IOException;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.http.Header;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/ResponseImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/ResponseImplTest.java
index 1c76979a7..76df3e648 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/ResponseImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/ResponseImplTest.java
@@ -14,8 +14,8 @@
 
 package org.apache.tapestry5.internal;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.internal.services.ResponseImpl;
 import org.apache.tapestry5.http.services.Response;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/ServletContextSymbolProviderTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/ServletContextSymbolProviderTest.java
index 5eec138e7..2e13bf4ae 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/ServletContextSymbolProviderTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/ServletContextSymbolProviderTest.java
@@ -19,7 +19,7 @@ import org.apache.tapestry5.ioc.services.SymbolProvider;
 import org.apache.tapestry5.test.ioc.TestBase;
 import org.testng.annotations.Test;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextImplTest.java
index 09f7f48c8..164b41435 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ContextImplTest.java
@@ -20,7 +20,7 @@ import org.apache.tapestry5.http.services.Context;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.testng.annotations.Test;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 import java.io.File;
 import java.io.IOException;
 import java.net.MalformedURLException;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java
index 1e4c67772..5022bb287 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java
@@ -19,7 +19,7 @@ import org.apache.tapestry5.internal.test.TestableRequestImpl;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.Cookie;
+import jakarta.servlet.http.Cookie;
 import java.util.ArrayList;
 import java.util.List;
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/IgnoredPathsFilterTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/IgnoredPathsFilterTest.java
index 171d0f0cb..ace61eb7b 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/IgnoredPathsFilterTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/IgnoredPathsFilterTest.java
@@ -20,8 +20,8 @@ import org.apache.tapestry5.http.services.HttpServletRequestHandler;
 import org.apache.tapestry5.test.TapestryTestCase;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.List;
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
index 3b6fb8dd3..c0d550811 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
@@ -20,8 +20,8 @@ import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
 import java.io.UnsupportedEncodingException;
 import java.util.Collections;
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RestSupportImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RestSupportImplTest.java
index d16939720..b9e8ca5aa 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RestSupportImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RestSupportImplTest.java
@@ -20,8 +20,9 @@ import java.io.InputStream;
 import java.util.Optional;
 import java.util.function.Consumer;
 
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.http.internal.services.RestSupportImpl;
 import org.apache.tapestry5.http.services.HttpRequestBodyConverter;
@@ -36,7 +37,7 @@ import org.testng.annotations.Test;
 public class RestSupportImplTest extends InternalBaseTestCase
 {
     final private static byte[] EMPTY_ARRAY = new byte[0];
-    
+
     @Test
     public void is_get() throws IOException
     {
@@ -48,7 +49,7 @@ public class RestSupportImplTest extends InternalBaseTestCase
     public void is_post() throws IOException
     {
         test("POST", EMPTY_ARRAY, (rs) -> assertTrue(rs.isHttpPost()));
-        test("notPOST", EMPTY_ARRAY, (rs) -> assertFalse(rs.isHttpPost()));        
+        test("notPOST", EMPTY_ARRAY, (rs) -> assertFalse(rs.isHttpPost()));
     }
 
     @Test
@@ -57,12 +58,12 @@ public class RestSupportImplTest extends InternalBaseTestCase
         test("HEAD", EMPTY_ARRAY, (rs) -> assertTrue(rs.isHttpHead()));
         test("notHEAD", EMPTY_ARRAY, (rs) -> assertFalse(rs.isHttpHead()));
     }
-    
+
     @Test
     public void is_put() throws IOException
     {
         test("PUT", EMPTY_ARRAY, (rs) -> assertTrue(rs.isHttpPut()));
-        test("notPUT", EMPTY_ARRAY, (rs) -> assertFalse(rs.isHttpPut()));        
+        test("notPUT", EMPTY_ARRAY, (rs) -> assertFalse(rs.isHttpPut()));
     }
 
     @Test
@@ -78,7 +79,7 @@ public class RestSupportImplTest extends InternalBaseTestCase
         test("PATCH", EMPTY_ARRAY, (rs) -> assertTrue(rs.isHttpPatch()));
         test("notPATCH", EMPTY_ARRAY, (rs) -> assertFalse(rs.isHttpPatch()));
     }
-    
+
     @Test
     public void get_request_body_as_result_provided()
     {
@@ -98,12 +99,12 @@ public class RestSupportImplTest extends InternalBaseTestCase
         Optional<String> result = restSupport.getRequestBodyAs(String.class);
         assertFalse(result.isPresent());
     }
-    
+
     final private static class TestHttpRequestBodyConverter implements HttpRequestBodyConverter
     {
         final private String value;
 
-        public TestHttpRequestBodyConverter(String value) 
+        public TestHttpRequestBodyConverter(String value)
         {
             super();
             this.value = value;
@@ -114,7 +115,7 @@ public class RestSupportImplTest extends InternalBaseTestCase
         public <T> T convert(HttpServletRequest request, Class<T> type) {
             return (T) value;
         }
-        
+
     }
 
     private void test(String method, byte[] body, Consumer<RestSupport> testCode) throws IOException
@@ -133,23 +134,39 @@ public class RestSupportImplTest extends InternalBaseTestCase
         EasyMock.replay(request);
         return request;
     }
-    
+
     final private static class TestServletInputStream extends ServletInputStream
     {
-        
         final private InputStream inputStream;
-        
-        public TestServletInputStream(byte[] bytes) 
+
+        public TestServletInputStream(byte[] bytes)
         {
             inputStream = new ByteArrayInputStream(bytes);
         }
 
         @Override
-        public int read() throws IOException 
+        public int read() throws IOException
         {
             return inputStream.read();
         }
-        
+
+
+        @Override
+        public boolean isFinished()
+        {
+            return false;
+        }
+
+        @Override
+        public boolean isReady()
+        {
+            return true;
+        }
+
+        @Override
+        public void setReadListener(ReadListener readListener)
+        {
+        }
     }
-    
+
 }
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
index 2f7cbd4ca..99434330c 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
@@ -22,8 +22,8 @@ import org.apache.tapestry5.http.services.SessionPersistedObjectAnalyzer;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StaticFilesFilterTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StaticFilesFilterTest.java
index 308d16953..10bb6403d 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StaticFilesFilterTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StaticFilesFilterTest.java
@@ -23,7 +23,7 @@ import org.apache.tapestry5.http.services.Response;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.net.URL;
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/test/TestableResponseImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/test/TestableResponseImplTest.java
index 9080d3061..40460a000 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/test/TestableResponseImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/test/TestableResponseImplTest.java
@@ -13,7 +13,7 @@
 // limitations under the License.
 package org.apache.tapestry5.internal.test;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.dom.Document;
 import org.apache.tapestry5.dom.Element;
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/services/HttpStatusTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/services/HttpStatusTest.java
index f759eae74..5d82b2b5d 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/services/HttpStatusTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/services/HttpStatusTest.java
@@ -21,7 +21,7 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import static org.testng.Assert.assertEquals;
 
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 
 public class HttpStatusTest
 {
diff --git a/tapestry-http/build.gradle b/tapestry-http/build.gradle
index 60d888b46..be6e3779c 100644
--- a/tapestry-http/build.gradle
+++ b/tapestry-http/build.gradle
@@ -12,10 +12,10 @@ dependencies {
     provided project(":tapestry-test")
     provided project(":tapestry-test-constants")
 
-    provided "javax.servlet:javax.servlet-api:${versions.servletapi}"
+    provided "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
 
     implementation "commons-codec:commons-codec:1.10"
-    implementation "commons-io:commons-io:2.11.0"
+    implementation "commons-io:commons-io:2.13.0"
 
     testRuntimeOnly "org.hsqldb:hsqldb:1.8.0.10"
 
@@ -29,4 +29,4 @@ jar {
     from("src/main/filtered-resources") {
         filter(ReplaceTokens, tokens: [version: project.parent.version])
     }
-}
\ No newline at end of file
+}
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandler.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandler.java
index 50151aa15..e37cd1410 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandler.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandler.java
@@ -15,9 +15,9 @@ import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import javax.servlet.AsyncListener;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.AsyncListener;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.internal.AsyncRequestService;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandlerResponse.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandlerResponse.java
index aec82c132..3b57f7778 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandlerResponse.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/AsyncRequestHandlerResponse.java
@@ -14,33 +14,33 @@ package org.apache.tapestry5.http;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
-import javax.servlet.AsyncListener;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.AsyncListener;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 /**
  * Class used by {@linkplain AsyncRequestHandler} to return information on how to handle
  * a request.
  * @see AsyncRequestHandler
  */
-public class AsyncRequestHandlerResponse 
+public class AsyncRequestHandlerResponse
 {
-    
-    private static final AsyncRequestHandlerResponse NOT_HANDLED = 
+
+    private static final AsyncRequestHandlerResponse NOT_HANDLED =
             new AsyncRequestHandlerResponse(false);
-    
+
     final private boolean async;
-    
+
     final private Executor executor;
-    
+
     private HttpServletRequest request;
-    
+
     private HttpServletResponse response;
-    
+
     private AsyncListener listener;
-    
+
     private long timeout;
-    
+
     /**
      * Creates an instance with a given {@link Executor}. It cannot be null.
      * If you want an instance with a non-async response, use {@link #notHandled()} instead.
@@ -50,14 +50,14 @@ public class AsyncRequestHandlerResponse
     {
         this(true, executor);
     }
-    
+
     private AsyncRequestHandlerResponse(boolean async, Executor executor)
     {
         Objects.requireNonNull(executor, "Parameter executor cannot be null");
         this.async = async;
         this.executor = executor;
     }
-    
+
     private AsyncRequestHandlerResponse(boolean async)
     {
         this.async = async;
@@ -65,7 +65,7 @@ public class AsyncRequestHandlerResponse
     }
 
     /**
-     * Defines a different request and response to be passed to {@link HttpServletRequest#startAsync(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}.
+     * Defines a different request and response to be passed to {@link HttpServletRequest#startAsync(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse)}.
      * Both cannot be null.
      */
     public AsyncRequestHandlerResponse with(HttpServletRequest request, HttpServletResponse response)
@@ -86,7 +86,7 @@ public class AsyncRequestHandlerResponse
         this.listener = listener;
         return this;
     }
-    
+
     /**
      * Sets the timeout for this asynchronous request in milliseconds.
      */
@@ -95,7 +95,7 @@ public class AsyncRequestHandlerResponse
         this.timeout = timeout;
         return this;
     }
-    
+
     /**
      * Returns a response saying this {@linkplain AsyncRequestHandler} doesn't handle this request.
      * @return an {@link AsyncRequestHandlerResponse}.
@@ -108,15 +108,15 @@ public class AsyncRequestHandlerResponse
     /**
      * Returns whether the request should be processed asynchronously or not.
      */
-    public boolean isAsync() 
+    public boolean isAsync()
     {
         return async;
     }
 
-    /**    
+    /**
      * Returns the {@link Executor} to be used to process the request.
      */
-    public Executor getExecutor() 
+    public Executor getExecutor()
     {
         return executor;
     }
@@ -124,7 +124,7 @@ public class AsyncRequestHandlerResponse
     /**
      * Returns the request to be used with {@link HttpServletRequest#startAsync()} or null.
      */
-    public HttpServletRequest getRequest() 
+    public HttpServletRequest getRequest()
     {
         return request;
     }
@@ -132,7 +132,7 @@ public class AsyncRequestHandlerResponse
     /**
      * Returns the response to be used with {@link HttpServletRequest#startAsync()} or null.
      */
-    public HttpServletResponse getResponse() 
+    public HttpServletResponse getResponse()
     {
         return response;
     }
@@ -140,11 +140,11 @@ public class AsyncRequestHandlerResponse
     /**
      * Returns the listener to be added to the asynchronous request or null.
      */
-    public AsyncListener getListener() 
+    public AsyncListener getListener()
     {
         return listener;
     }
-    
+
     /**
      * Returns whether a request and a response were set in this object.
      */
@@ -162,10 +162,10 @@ public class AsyncRequestHandlerResponse
     }
 
     @Override
-    public String toString() 
+    public String toString()
     {
         return "AsyncRequestHandlerResponse [async=" + async + ", executor=" + executor + ", request=" + request + ", response=" + response + ", listener="
                 + listener + "]";
     }
-    
+
 }
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/CorsHandlerResult.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/CorsHandlerResult.java
index 0f7c3f967..5144044ac 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/CorsHandlerResult.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/CorsHandlerResult.java
@@ -17,8 +17,8 @@ import org.apache.tapestry5.http.services.CorsHttpServletRequestFilter;
 import org.apache.tapestry5.http.services.HttpServletRequestFilter;
 
 /**
- * Enumeration that defines the possible outcomes of 
- * {@link CorsHandler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+ * Enumeration that defines the possible outcomes of
+ * {@link CorsHandler#handle(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse)}
  * @see CorsHandler
  * @see CorsHttpServletRequestFilter
  * @since 5.8.2
@@ -26,19 +26,19 @@ import org.apache.tapestry5.http.services.HttpServletRequestFilter;
 public enum CorsHandlerResult
 {
     /**
-     * The next {@linkplain CorsHandler} should be called. 
+     * The next {@linkplain CorsHandler} should be called.
      * Usually returned when the CORS handler didn't actually processed the request.
      */
     CONTINUE_CORS_PROCESSING,
-    
+
     /**
      * No other {@linkplain CorsHandler} should be called and request processing
-     * should resume (i.e. {@linkplain CorsHttpServletRequestFilter} calls 
-     * {@link HttpServletRequestFilter#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.apache.tapestry5.http.services.HttpServletRequestHandler)}.
+     * should resume (i.e. {@linkplain CorsHttpServletRequestFilter} calls
+     * {@link HttpServletRequestFilter#service(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse, org.apache.tapestry5.http.services.HttpServletRequestHandler)}.
      * Usually returned when the handler actually processed the request.
      */
     CONTINUE_REQUEST_PROCESSING,
-    
+
     /**
      * The request processing should stop immediately. Usually used with CORS preflight requests.
      */
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryFilter.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryFilter.java
index 3fdb98dff..fc096e01b 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryFilter.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryFilter.java
@@ -14,16 +14,16 @@ package org.apache.tapestry5.http;
 
 import java.io.IOException;
 
-import javax.servlet.AsyncContext;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.internal.AsyncRequestService;
 import org.apache.tapestry5.http.internal.ServletContextSymbolProvider;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryHttpSymbolConstants.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryHttpSymbolConstants.java
index a0a4b3591..1f3faa62b 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryHttpSymbolConstants.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/TapestryHttpSymbolConstants.java
@@ -61,7 +61,7 @@ final public class TapestryHttpSymbolConstants {
      */
     public static final String CLUSTERED_SESSIONS = "tapestry.clustered-sessions";
     /**
-     * If true (the default), then Tapestry will apply locking semantics around access to the {@link javax.servlet.http.HttpSession}.
+     * If true (the default), then Tapestry will apply locking semantics around access to the {@link jakarta.servlet.http.HttpSession}.
      * Reading attribute names occurs with a shared read lock; getting or setting an attribute upgrades to an exclusive write lock.
      * This can tend to serialize threads when a number of simultaneous (Ajax) requests from the client arrive ... however,
      * many implementations of HttpSession are not thread safe, and often mutable objects are stored in the session and shared
@@ -82,7 +82,7 @@ final public class TapestryHttpSymbolConstants {
      * @since 5.1.0.0
      */
     public static final String APPLICATION_VERSION = "tapestry.application-version";
-    
+
     /**
      * Indicates whether Tapestry is running in production mode or developer mode. This affects a large
      * number of Tapestry behaviors related to performance and security, including how exceptions are
@@ -91,15 +91,15 @@ final public class TapestryHttpSymbolConstants {
      * variables (to assist with debugging), and more.
      */
     public static final String PRODUCTION_MODE = "tapestry.production-mode";
-    
+
     /**
      * The version number of the core Tapestry framework, or UNKNOWN if the version number is not available (which
      * should only occur when developing Tapestry).
      */
     public static final String TAPESTRY_VERSION = "tapestry.version";
-    
+
     /**
-     * Identifies the context path of the application, as determined from {@link javax.servlet.ServletContext#getContextPath()}.
+     * Identifies the context path of the application, as determined from {@link jakarta.servlet.ServletContext#getContextPath()}.
      * This is either a blank string or a string that starts with a slash but does not end with one.
      *
      * @since 5.4
@@ -137,7 +137,7 @@ final public class TapestryHttpSymbolConstants {
      */
     public static final String GZIP_COMPRESSION_ENABLED = "tapestry.gzip-compression-enabled";
     /**
-     * Defines whether the CORS (Cross-Origing Resource Sharing) support 
+     * Defines whether the CORS (Cross-Origing Resource Sharing) support
      * should be enabled or not. Default value is "false". If you set this to "true",
      * you should also set {@link #CORS_ALLOWED_ORIGINS}.
      * @see CorsHandler
@@ -148,49 +148,49 @@ final public class TapestryHttpSymbolConstants {
 
     /**
      * Comma-delimited of origins allowed for CORS. The special value "*" means allowing all origins.
-     * This is used by the default implementation of {@link CorsHandlerHelper#getAllowedOrigin(javax.servlet.http.HttpServletRequest)}.
+     * This is used by the default implementation of {@link CorsHandlerHelper#getAllowedOrigin(jakarta.servlet.http.HttpServletRequest)}.
      * Default value is the empty string (i.e. no origins allowed and CORS actually disabled).
      * @since 5.8.2
      */
     public static final String CORS_ALLOWED_ORIGINS = "tapestry.cors-allowed-origins";
-    
+
     /**
      * Boolean value defining whether the Access-Control-Allow-Credentials HTTP header
      * should be set automatically in the response for CORS requests. Default value is
-     * <code>false</code>. This is used by the default implementation of {@link CorsHandlerHelper#configureCredentials(javax.servlet.http.HttpServletResponse)}.
+     * <code>false</code>. This is used by the default implementation of {@link CorsHandlerHelper#configureCredentials(jakarta.servlet.http.HttpServletResponse)}.
      * @since 5.8.2
      */
     public static final String CORS_ALLOW_CREDENTIALS = "tapestry.cors-allow-credentials";
 
     /**
      * Value to be used in the Access-Control-Allow-Methods in CORS preflight request responses.
-     * This is used by {@link CorsHandlerHelper#configureMethods(javax.servlet.http.HttpServletResponse)}.
+     * This is used by {@link CorsHandlerHelper#configureMethods(jakarta.servlet.http.HttpServletResponse)}.
      * Default value is <code>GET,HEAD,PUT,PATCH,POST,DELETE</code>.
      * @since 5.8.2
      */
     public static final String CORS_ALLOW_METHODS = "tapestry.cors-allow-methods";
-    
+
     /**
      * Value to be used in the Access-Control-Allow-Headers in CORS preflight request responses.
-     * This is used by {@link CorsHandlerHelper#configureAllowedHeaders(javax.servlet.http.HttpServletResponse, javax.servlet.http.HttpServletRequest)},
+     * This is used by {@link CorsHandlerHelper#configureAllowedHeaders(jakarta.servlet.http.HttpServletResponse, jakarta.servlet.http.HttpServletRequest)},
      * which only sets the header if the value isn't empty.
      * Default value is the empty string.
      * @since 5.8.2
      */
     public static final String CORS_ALLOWED_HEADERS = "tapestry.cors-allowed-headers";
-    
+
     /**
      * Value to be used in the Access-Control-Expose-Headers in CORS preflight request responses.
-     * This is used by the default implementation of {@link CorsHandlerHelper#configureExposeHeaders(javax.servlet.http.HttpServletResponse)},
+     * This is used by the default implementation of {@link CorsHandlerHelper#configureExposeHeaders(jakarta.servlet.http.HttpServletResponse)},
      * which only sets the header if the value isn't empty.
      * Default value is the empty string.
      * @since 5.8.2
      */
     public static final String CORS_EXPOSE_HEADERS = "tapestry.cors-expose-headers";
-    
+
     /**
      * Value to be used in the Access-Control-Max-Age in responses to preflight CORS requests.
-     * This is used by {@link CorsHandlerHelper#configureMaxAge(javax.servlet.http.HttpServletResponse)},
+     * This is used by {@link CorsHandlerHelper#configureMaxAge(jakarta.servlet.http.HttpServletResponse)},
      * which only sets the header if the value isn't empty.
      * Default value is the empty string.
      * This is an alias for {@link TapestryHttpSymbolConstants#CORS_MAX_AGE}.
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/AsyncRequestService.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/AsyncRequestService.java
index a7f4d52bb..98f854a80 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/AsyncRequestService.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/AsyncRequestService.java
@@ -11,8 +11,8 @@
 // limitations under the License.
 package org.apache.tapestry5.http.internal;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.AsyncRequestHandler;
 import org.apache.tapestry5.http.AsyncRequestHandlerResponse;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/ServletContextSymbolProvider.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/ServletContextSymbolProvider.java
index a827791c5..8384b444f 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/ServletContextSymbolProvider.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/ServletContextSymbolProvider.java
@@ -18,7 +18,7 @@ import org.apache.tapestry5.commons.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.services.SymbolProvider;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 import java.util.Map;
 
 /**
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/TypeCoercerHttpRequestBodyConverter.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/TypeCoercerHttpRequestBodyConverter.java
index b3d48f590..f6869e89e 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/TypeCoercerHttpRequestBodyConverter.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/TypeCoercerHttpRequestBodyConverter.java
@@ -11,7 +11,7 @@
 // limitations under the License.
 package org.apache.tapestry5.http.internal;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.commons.internal.util.TapestryException;
 import org.apache.tapestry5.commons.services.TypeCoercer;
@@ -46,4 +46,4 @@ final public class TypeCoercerHttpRequestBodyConverter implements HttpRequestBod
         return value;
     }
     
-}
\ No newline at end of file
+}
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/BufferedGZipOutputStream.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/BufferedGZipOutputStream.java
index 76aa3410d..c83fbe14e 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/BufferedGZipOutputStream.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/BufferedGZipOutputStream.java
@@ -20,8 +20,9 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.zip.GZIPOutputStream;
 
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.WriteListener;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.internal.TapestryHttpInternalConstants;
 import org.apache.tapestry5.http.services.CompressionAnalyzer;
@@ -84,7 +85,7 @@ public class BufferedGZipOutputStream extends ServletOutputStream
         if (useCompression)
         {
             response.setHeader(
-                    TapestryHttpInternalConstants.CONTENT_ENCODING_HEADER, 
+                    TapestryHttpInternalConstants.CONTENT_ENCODING_HEADER,
                     TapestryHttpInternalConstants.GZIP_CONTENT_ENCODING);
         }
 
@@ -143,4 +144,15 @@ public class BufferedGZipOutputStream extends ServletOutputStream
 
         return currentOutputStream;
     }
+
+    @Override
+    public boolean isReady()
+    {
+        return true;
+    }
+
+    @Override
+    public void setWriteListener(WriteListener writeListener)
+    {
+    }
 }
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZIPEnabledResponse.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZIPEnabledResponse.java
index 330f2bd84..6723a3f03 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZIPEnabledResponse.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZIPEnabledResponse.java
@@ -16,10 +16,10 @@ package org.apache.tapestry5.http.internal.gzip;
 
 import java.io.IOException;
 
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponseWrapper;
 
 import org.apache.tapestry5.http.TapestryHttpConstants;
 import org.apache.tapestry5.http.services.CompressionAnalyzer;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZipFilter.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZipFilter.java
index dab055103..ced140174 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZipFilter.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/gzip/GZipFilter.java
@@ -16,8 +16,8 @@ package org.apache.tapestry5.http.internal.gzip;
 
 import java.io.IOException;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
 import org.apache.tapestry5.http.services.CompressionAnalyzer;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ApplicationGlobalsImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ApplicationGlobalsImpl.java
index ac06bce1e..d4153a1b6 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ApplicationGlobalsImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ApplicationGlobalsImpl.java
@@ -17,7 +17,7 @@ package org.apache.tapestry5.http.internal.services;
 import org.apache.tapestry5.http.services.ApplicationGlobals;
 import org.apache.tapestry5.http.services.Context;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 public class ApplicationGlobalsImpl implements ApplicationGlobals
 {
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/AsyncRequestServiceImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/AsyncRequestServiceImpl.java
index 964aa6085..b6e72549a 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/AsyncRequestServiceImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/AsyncRequestServiceImpl.java
@@ -13,8 +13,8 @@ package org.apache.tapestry5.http.internal.services;
 
 import java.util.List;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.AsyncRequestHandler;
 import org.apache.tapestry5.http.AsyncRequestHandlerResponse;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ClusteredSessionImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ClusteredSessionImpl.java
index 29099bf62..2a575f812 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ClusteredSessionImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ClusteredSessionImpl.java
@@ -18,8 +18,8 @@ import org.apache.tapestry5.commons.util.CollectionFactory;
 import org.apache.tapestry5.http.services.SessionPersistedObjectAnalyzer;
 import org.apache.tapestry5.ioc.services.PerthreadManager;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
 import java.util.Map;
 
 /**
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ContextImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ContextImpl.java
index be1ced0aa..7c194c804 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ContextImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ContextImpl.java
@@ -19,7 +19,7 @@ import org.apache.tapestry5.commons.util.Stack;
 import org.apache.tapestry5.http.services.Context;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 import java.io.File;
 import java.net.MalformedURLException;
 import java.net.URL;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImpl.java
index f32b29400..0875257e9 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImpl.java
@@ -15,8 +15,8 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
 import org.apache.tapestry5.http.services.CorsHandler;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandler.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandler.java
index b14f9a284..fed6d529f 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandler.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandler.java
@@ -14,8 +14,8 @@ package org.apache.tapestry5.http.internal.services;
 import java.io.IOException;
 import java.util.Optional;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.CorsHandlerResult;
 import org.apache.tapestry5.http.services.CorsHandler;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestGlobalsImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestGlobalsImpl.java
index 66d67038e..8a1a610fc 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestGlobalsImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestGlobalsImpl.java
@@ -20,8 +20,8 @@ import org.apache.tapestry5.http.services.Response;
 import org.apache.tapestry5.ioc.ScopeConstants;
 import org.apache.tapestry5.ioc.annotations.Scope;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 /**
  * Dumb data holder for per-request data.
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestImpl.java
index 6694a14b3..547c673b0 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RequestImpl.java
@@ -18,7 +18,7 @@ import java.util.Enumeration;
 import java.util.List;
 import java.util.Locale;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.commons.util.CollectionFactory;
 import org.apache.tapestry5.http.services.Request;
@@ -108,6 +108,7 @@ public class RequestImpl implements Request
         return pathInfo.length() == 0 ? "/" : pathInfo;
     }
 
+    @Override
     public String getContextPath()
     {
         return request.getContextPath();
@@ -234,7 +235,7 @@ public class RequestImpl implements Request
     {
         return request.getRemoteHost();
     }
-    
+
     /**
      * Converts an enumeration (of Strings) into a sorted list of Strings.
      */
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseCompressionAnalyzerImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseCompressionAnalyzerImpl.java
index 80a14083a..22487fe2b 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseCompressionAnalyzerImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseCompressionAnalyzerImpl.java
@@ -12,7 +12,7 @@
 
 package org.apache.tapestry5.http.internal.services;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.commons.util.CommonsUtils;
 import org.apache.tapestry5.http.ContentType;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseImpl.java
index bf2c27e97..67148a662 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/ResponseImpl.java
@@ -21,8 +21,8 @@ import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.Writer;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.Link;
 import org.apache.tapestry5.http.TapestryHttpConstants;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RestSupportImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RestSupportImpl.java
index 5831af5ee..337b2b3a5 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RestSupportImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/RestSupportImpl.java
@@ -15,7 +15,7 @@ package org.apache.tapestry5.http.internal.services;
 
 import java.util.Optional;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.http.services.HttpRequestBodyConverter;
 import org.apache.tapestry5.http.services.RestSupport;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/SessionImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/SessionImpl.java
index 01047d51b..bc938e97e 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/SessionImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/SessionImpl.java
@@ -19,8 +19,8 @@ import org.apache.tapestry5.http.services.Session;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.services.PerthreadManager;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/TapestrySessionFactoryImpl.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/TapestrySessionFactoryImpl.java
index 9cd5c96b2..4ed6f3b8b 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/TapestrySessionFactoryImpl.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/internal/services/TapestrySessionFactoryImpl.java
@@ -20,8 +20,8 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
 
 import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
 import org.apache.tapestry5.http.services.Session;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/modules/TapestryHttpModule.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/modules/TapestryHttpModule.java
index fae0b50b3..c3e3777d2 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/modules/TapestryHttpModule.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/modules/TapestryHttpModule.java
@@ -19,9 +19,9 @@ import java.io.Reader;
 import java.util.List;
 import java.util.Map;
 
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.tapestry5.commons.MappedConfiguration;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ApplicationGlobals.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ApplicationGlobals.java
index ab4f6ee6a..17a310ee5 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ApplicationGlobals.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ApplicationGlobals.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.http.services;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 /**
  * Stores global information about the application and its environment.
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandler.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandler.java
index 6d8b790fa..cb9688589 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandler.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandler.java
@@ -14,8 +14,8 @@ package org.apache.tapestry5.http.services;
 
 import java.io.IOException;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.CorsHandlerResult;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandlerHelper.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandlerHelper.java
index f0cc8e1ad..92c1f8442 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandlerHelper.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHandlerHelper.java
@@ -13,8 +13,8 @@ package org.apache.tapestry5.http.services;
 
 import java.util.Optional;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHttpServletRequestFilter.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHttpServletRequestFilter.java
index 07d3deb26..1fd3e9cda 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHttpServletRequestFilter.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/CorsHttpServletRequestFilter.java
@@ -16,8 +16,8 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.CorsHandlerResult;
 import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpRequestBodyConverter.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpRequestBodyConverter.java
index 9eb037ac1..f3628aec2 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpRequestBodyConverter.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpRequestBodyConverter.java
@@ -13,7 +13,7 @@
 // limitations under the License.
 package org.apache.tapestry5.http.services;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestFilter.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestFilter.java
index f2611b983..1eafbb6eb 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestFilter.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestFilter.java
@@ -14,8 +14,8 @@
 
 package org.apache.tapestry5.http.services;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import java.io.IOException;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestHandler.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestHandler.java
index e3a8dd579..981926ef0 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestHandler.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/HttpServletRequestHandler.java
@@ -16,8 +16,8 @@ package org.apache.tapestry5.http.services;
 
 import java.io.IOException;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RequestGlobals.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RequestGlobals.java
index 6dcd4f59d..0fd9c65b9 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RequestGlobals.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RequestGlobals.java
@@ -14,8 +14,8 @@
 
 package org.apache.tapestry5.http.services;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 /**
  * Service used to store the current request objects, both the Servlet API versions, and the
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RestSupport.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RestSupport.java
index cc13e109d..ef57ed780 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RestSupport.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/RestSupport.java
@@ -16,7 +16,7 @@ package org.apache.tapestry5.http.services;
 
 import java.util.Optional;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.commons.services.TypeCoercer;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializer.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializer.java
index 3aba6416b..d525e5d06 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializer.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializer.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.http.services;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
 
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializerFilter.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializerFilter.java
index 762532a04..2551098c1 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializerFilter.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/ServletApplicationInitializerFilter.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.http.services;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 /**
  * Filter interface for {@link org.apache.tapestry5.http.services.ServletApplicationInitializer}.
diff --git a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/Session.java b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/Session.java
index 70019a1d7..7cf0d14ab 100644
--- a/tapestry-http/src/main/java/org/apache/tapestry5/http/services/Session.java
+++ b/tapestry-http/src/main/java/org/apache/tapestry5/http/services/Session.java
@@ -16,7 +16,7 @@ package org.apache.tapestry5.http.services;
 
 import java.util.List;
 
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpSession;
 
 import org.apache.tapestry5.http.OptimizedSessionPersistedObject;
 import org.apache.tapestry5.http.annotations.ImmutableSessionPersistedObject;
diff --git a/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImplTest.java b/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImplTest.java
index 10632f99b..865bb13ab 100644
--- a/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImplTest.java
+++ b/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHandlerHelperImplTest.java
@@ -7,10 +7,10 @@ import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.services.CorsHandlerHelper;
 import org.easymock.EasyMock;
@@ -18,22 +18,22 @@ import org.junit.Assert;
 import org.testng.annotations.Test;
 
 /**
- * Test suite for {@link CorsHandlerHelperImpl.
+ * Test suite for {@link CorsHandlerHelperImpl}.
  */
 public class CorsHandlerHelperImplTest {
 
-    
+
     @Test
     public void get_origin()
     {
-        
+
         final String[] allowedOrigins = new String[] {"http://tapestry.apache.org", "https://apache.org"};
-        
+
         // Scenario 1: explicit origins
-        
+
         CorsHandlerHelper helper = create(String.join("  ,  ", allowedOrigins));
         HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
-        
+
         for (String origin : allowedOrigins)
         {
 
@@ -43,18 +43,18 @@ public class CorsHandlerHelperImplTest {
             EasyMock.replay(request);
             Assert.assertEquals("Allowed origin", origin, helper.getAllowedOrigin(request).get());
             EasyMock.verify(request);
-            
+
             // Non-allowed origins
             EasyMock.reset(request);
             EasyMock.expect(request.getHeader(CorsHandlerHelper.ORIGIN_HEADER)).andReturn(origin + "baaaa");
             EasyMock.replay(request);
             Assert.assertFalse("Non-allowed origins", helper.getAllowedOrigin(request).isPresent());
             EasyMock.verify(request);
-            
+
         }
-        
+
         // Scenario 2: allow all origins
-        
+
         helper = create(CorsHandlerHelper.ORIGIN_WILDCARD);
         for (int i = 0; i < 10; i++)
         {
@@ -65,13 +65,13 @@ public class CorsHandlerHelperImplTest {
             Assert.assertEquals("All origins should be accepted", CorsHandlerHelper.ORIGIN_WILDCARD, helper.getAllowedOrigin(request).get());
             EasyMock.verify(request);
         }
-        
+
     }
 
     @Test
     public void is_preflight()
     {
-        
+
         // Scenario 1: not OPTIONS
         final String origin = "https://tapestry.apache.org";
         CorsHandlerHelper helper = create(origin);
@@ -106,20 +106,20 @@ public class CorsHandlerHelperImplTest {
         EasyMock.verify(request);
 
     }
-    
+
     @Test
     public void get_path()
     {
 
         CorsHandlerHelper helper = create("*");
         HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
-        
+
         final String pathInfo = "/blah/foo/";
         EasyMock.expect(request.getPathInfo()).andReturn(pathInfo);
         EasyMock.replay(request);
         Assert.assertEquals(pathInfo, helper.getPath(request));
         EasyMock.verify(request);
-        
+
         EasyMock.reset(request);
         EasyMock.expect(request.getPathInfo()).andReturn("");
         EasyMock.replay(request);
@@ -135,19 +135,19 @@ public class CorsHandlerHelperImplTest {
         EasyMock.verify(request);
 
     }
-    
+
     @Test
     public void configure_origin()
     {
         CorsHandlerHelper helper = create("*");
         HttpServletResponse response = new TestableHttpServletResponse();;
-        
+
         final String origin = "https://apache.org";
         helper.configureOrigin(response, origin);
         Assert.assertEquals(origin, response.getHeader(CorsHandlerHelper.ALLOW_ORIGIN_HEADER));
         Assert.assertEquals(CorsHandlerHelper.ORIGIN_HEADER, response.getHeader(CorsHandlerHelper.VARY_HEADER));
     }
-    
+
     @Test
     public void add_value_to_vary_header()
     {
@@ -159,30 +159,30 @@ public class CorsHandlerHelperImplTest {
         }
         Assert.assertEquals(String.join(", ", values), response.getHeader(CorsHandlerHelper.VARY_HEADER));
     }
-    
+
     @Test
     public void configure_credentials()
     {
         HttpServletResponse response = new TestableHttpServletResponse();
-        
+
         CorsHandlerHelper helper = create(CorsHandlerHelper.ORIGIN_WILDCARD, true);
         helper.configureCredentials(response);
         Assert.assertEquals("true", response.getHeader(CorsHandlerHelper.ALLOW_CREDENTIALS_HEADER));
-        
+
         response = new TestableHttpServletResponse();
 
         helper = create(CorsHandlerHelper.ORIGIN_WILDCARD, false);
         helper.configureCredentials(response);
-        Assert.assertNull("Header shouldn't be set", 
+        Assert.assertNull("Header shouldn't be set",
                 response.getHeader(CorsHandlerHelper.ALLOW_CREDENTIALS_HEADER));
 
     }
-    
+
     @Test
     public void configure_methods()
     {
         HttpServletResponse response = new TestableHttpServletResponse();
-        
+
         final String allowedMethods = "GET,PUT";
         CorsHandlerHelper helper = new CorsHandlerHelperImpl("", false, allowedMethods, "", "", "");
         response = new TestableHttpServletResponse();
@@ -195,7 +195,7 @@ public class CorsHandlerHelperImplTest {
     {
         HttpServletResponse response = new TestableHttpServletResponse();
         HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
-     
+
         // Scenario 1: configuration isn't empty
         final String configuredHeaders = "Blah,Foo";
         CorsHandlerHelper helper = new CorsHandlerHelperImpl("", false, "", configuredHeaders, "", "");
@@ -233,17 +233,17 @@ public class CorsHandlerHelperImplTest {
     public void configure_expose_headers()
     {
         HttpServletResponse response = new TestableHttpServletResponse();
-        
+
         final String exposeHeaders = "Blah,Foo,Bar";
         CorsHandlerHelper helper = new CorsHandlerHelperImpl("", false, "", "", exposeHeaders, "");
         helper.configureExposeHeaders(response);
         Assert.assertEquals(exposeHeaders, response.getHeader(CorsHandlerHelper.EXPOSE_HEADERS_HEADER));
-        
+
         response = new TestableHttpServletResponse();
 
         helper = new CorsHandlerHelperImpl("", false, "", "", "", "");
         helper.configureExposeHeaders(response);
-        Assert.assertNull("Header shouldn't be set", 
+        Assert.assertNull("Header shouldn't be set",
                 response.getHeader(CorsHandlerHelper.EXPOSE_HEADERS_HEADER));
 
     }
@@ -252,17 +252,17 @@ public class CorsHandlerHelperImplTest {
     public void configure_max_age()
     {
         HttpServletResponse response = new TestableHttpServletResponse();
-        
+
         final String maxAge = "1234567";
         CorsHandlerHelper helper = new CorsHandlerHelperImpl("", false, "", "", "", maxAge);
         helper.configureMaxAge(response);
         Assert.assertEquals(maxAge, response.getHeader(CorsHandlerHelper.MAX_AGE_HEADER));
-        
+
         response = new TestableHttpServletResponse();
 
         helper = new CorsHandlerHelperImpl("", false, "", "", "", "");
         helper.configureExposeHeaders(response);
-        Assert.assertNull("Header shouldn't be set", 
+        Assert.assertNull("Header shouldn't be set",
                 response.getHeader(CorsHandlerHelper.MAX_AGE_HEADER));
 
     }
@@ -271,211 +271,217 @@ public class CorsHandlerHelperImplTest {
     {
         return create(allowedOrigins, false);
     }
-    
+
     private CorsHandlerHelper create(String allowedOrigins, boolean allowCredentials)
     {
         return new CorsHandlerHelperImpl(allowedOrigins, allowCredentials, "", "", "", "");
     }
-    
+
     private final class TestableHttpServletResponse implements HttpServletResponse
     {
-        
+
         private final Map<String, String> headers = new HashMap<>();
 
         @Override
-        public String getCharacterEncoding() 
+        public String getCharacterEncoding()
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public String getContentType() 
+        public String getContentType()
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public ServletOutputStream getOutputStream() throws IOException 
+        public ServletOutputStream getOutputStream() throws IOException
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public PrintWriter getWriter() throws IOException 
+        public PrintWriter getWriter() throws IOException
         {
             return null;
         }
 
         @Override
-        public void setCharacterEncoding(String charset) 
+        public void setCharacterEncoding(String charset)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setContentLength(int len) 
+        public void setContentLength(int len)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setContentType(String type) 
+        public void setContentLengthLong(long len)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setBufferSize(int size) 
+        public void setContentType(String type)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public int getBufferSize() 
+        public void setBufferSize(int size)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void flushBuffer() throws IOException 
+        public int getBufferSize()
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void resetBuffer() 
+        public void flushBuffer() throws IOException
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public boolean isCommitted() 
+        public void resetBuffer()
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isCommitted()
         {
             return false;
         }
 
         @Override
-        public void reset() 
+        public void reset()
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setLocale(Locale loc) 
+        public void setLocale(Locale loc)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public Locale getLocale() 
+        public Locale getLocale()
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void addCookie(Cookie cookie) 
+        public void addCookie(Cookie cookie)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public boolean containsHeader(String name) 
+        public boolean containsHeader(String name)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public String encodeURL(String url) 
+        public String encodeURL(String url)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public String encodeRedirectURL(String url) 
+        public String encodeRedirectURL(String url)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public String encodeUrl(String url) 
+        public String encodeUrl(String url)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public String encodeRedirectUrl(String url) 
+        public String encodeRedirectUrl(String url)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void sendError(int sc, String msg) throws IOException 
+        public void sendError(int sc, String msg) throws IOException
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void sendError(int sc) throws IOException 
+        public void sendError(int sc) throws IOException
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void sendRedirect(String location) throws IOException 
+        public void sendRedirect(String location) throws IOException
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setDateHeader(String name, long date) 
+        public void setDateHeader(String name, long date)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void addDateHeader(String name, long date) 
+        public void addDateHeader(String name, long date)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setHeader(String name, String value) 
+        public void setHeader(String name, String value)
         {
             headers.put(name, value);
         }
 
         @Override
-        public void addHeader(String name, String value) 
+        public void addHeader(String name, String value)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setIntHeader(String name, int value) 
+        public void setIntHeader(String name, int value)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void addIntHeader(String name, int value) 
+        public void addIntHeader(String name, int value)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setStatus(int sc) 
+        public void setStatus(int sc)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void setStatus(int sc, String sm) 
+        public void setStatus(int sc, String sm)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public int getStatus() 
+        public int getStatus()
         {
             throw new UnsupportedOperationException();
         }
@@ -486,17 +492,17 @@ public class CorsHandlerHelperImplTest {
         }
 
         @Override
-        public Collection<String> getHeaders(String name) 
+        public Collection<String> getHeaders(String name)
         {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public Collection<String> getHeaderNames() 
+        public Collection<String> getHeaderNames()
         {
             throw new UnsupportedOperationException();
         }
-        
+
     }
-    
+
 }
diff --git a/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHttpServletRequestFilterTest.java b/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHttpServletRequestFilterTest.java
index c39334425..45f319c62 100644
--- a/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHttpServletRequestFilterTest.java
+++ b/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/CorsHttpServletRequestFilterTest.java
@@ -3,8 +3,8 @@ package org.apache.tapestry5.http.internal.services;
 import java.io.IOException;
 import java.util.Arrays;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.CorsHandlerResult;
 import org.apache.tapestry5.http.services.CorsHandler;
diff --git a/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandlerTest.java b/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandlerTest.java
index e711e4591..411c6b3d3 100644
--- a/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandlerTest.java
+++ b/tapestry-http/src/test/java/org/apache/tapestry5/http/internal/services/DefaultCorsHandlerTest.java
@@ -3,8 +3,8 @@ package org.apache.tapestry5.http.internal.services;
 import java.io.IOException;
 import java.util.Optional;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.CorsHandlerResult;
 import org.apache.tapestry5.http.services.CorsHandlerHelper;
diff --git a/tapestry-http/src/test/java/org/apache/tapestry5/http/test/TestHttpServletRequestFilter.java b/tapestry-http/src/test/java/org/apache/tapestry5/http/test/TestHttpServletRequestFilter.java
index 1c96b9620..4c8c1d1cb 100644
--- a/tapestry-http/src/test/java/org/apache/tapestry5/http/test/TestHttpServletRequestFilter.java
+++ b/tapestry-http/src/test/java/org/apache/tapestry5/http/test/TestHttpServletRequestFilter.java
@@ -2,8 +2,8 @@ package org.apache.tapestry5.http.test;
 
 import java.io.IOException;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.http.services.HttpServletRequestFilter;
 import org.apache.tapestry5.http.services.HttpServletRequestHandler;
diff --git a/tapestry-http/src/test/webapp/longfile.txt b/tapestry-http/src/test/webapp/longfile.txt
index 3e441c1d0..0698fc88b 100644
--- a/tapestry-http/src/test/webapp/longfile.txt
+++ b/tapestry-http/src/test/webapp/longfile.txt
@@ -30,9 +30,9 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.regex.Pattern;
 
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.BindingConstants;
@@ -2152,7 +2152,7 @@ public final class TapestryModule
         configuration.add(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_NAME, "");
         configuration.add(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_CSS_CLASS, "");
         configuration.add(SymbolConstants.FORM_FIELD_CSS_CLASS, "form-control");
-        
+
         // Grid's default table CSS class comes from the ComponentParameterConstants.GRID_TABLE_CSS_CLASS symbol
         configuration.add(SymbolConstants.BEAN_DISPLAY_CSS_CLASS, "well dl-horizontal");
         configuration.add(SymbolConstants.BEAN_EDITOR_BOOLEAN_PROPERTY_DIV_CSS_CLASS, "input-group");
@@ -2731,7 +2731,7 @@ public final class TapestryModule
 
                 info = new ComponentLibraryInfo();
 
-                // the information above will probably not change in the future, or change very 
+                // the information above will probably not change in the future, or change very
                 // infrequently, so I see no problem in hardwiring them here.
                 info.setArtifactId("tapestry-core");
                 info.setGroupId("org.apache.tapestry");
@@ -2764,5 +2764,5 @@ public final class TapestryModule
             return info;
         }
     }
-    
+
 }
diff --git a/tapestry-kaptcha/build.gradle b/tapestry-kaptcha/build.gradle
index 4b79ed964..575ed8657 100644
--- a/tapestry-kaptcha/build.gradle
+++ b/tapestry-kaptcha/build.gradle
@@ -5,7 +5,7 @@ dependencies {
 
     implementation "com.github.axet:kaptcha:0.0.8"
 
-    provided "javax.servlet:javax.servlet-api:${versions.servletapi}"
+    provided "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
 
     testImplementation project(':tapestry-test')
 }
@@ -14,4 +14,4 @@ jar {
     manifest {
         attributes 'Tapestry-Module-Classes': 'org.apache.tapestry5.kaptcha.modules.KaptchaModule'
     }
-}
\ No newline at end of file
+}
diff --git a/tapestry-kaptcha/src/main/java/org/apache/tapestry5/kaptcha/components/KaptchaImage.java b/tapestry-kaptcha/src/main/java/org/apache/tapestry5/kaptcha/components/KaptchaImage.java
index 1659a8694..0a44d06b8 100644
--- a/tapestry-kaptcha/src/main/java/org/apache/tapestry5/kaptcha/components/KaptchaImage.java
+++ b/tapestry-kaptcha/src/main/java/org/apache/tapestry5/kaptcha/components/KaptchaImage.java
@@ -24,7 +24,7 @@ import org.apache.tapestry5.kaptcha.services.KaptchaProducer;
 import org.apache.tapestry5.services.HttpError;
 
 import javax.imageio.ImageIO;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
diff --git a/tapestry-latest-java-tests/build.gradle b/tapestry-latest-java-tests/build.gradle
index 4371b82ff..570c98fce 100644
--- a/tapestry-latest-java-tests/build.gradle
+++ b/tapestry-latest-java-tests/build.gradle
@@ -3,7 +3,7 @@ description = "Test suite for making sure Tapestry runs on latest Java"
 sourceCompatibility = "17"
 targetCompatibility = "17"
 
-tasks.withType(JavaCompile).each { 
+tasks.withType(JavaCompile).each {
     it.options.compilerArgs.add("--enable-preview")
 }
 
diff --git a/tapestry-openapi-viewer/build.gradle b/tapestry-openapi-viewer/build.gradle
index b6a62e6ff..ad38826ca 100644
--- a/tapestry-openapi-viewer/build.gradle
+++ b/tapestry-openapi-viewer/build.gradle
@@ -2,7 +2,7 @@ description = "Embedded OpenAPI (Swagger) viewer for Tapestry, based on Swagger
 
 dependencies {
     implementation project(':tapestry-core')
-    testImplementation "javax.servlet:javax.servlet-api:${versions.servletapi}"
+    testImplementation "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
 }
 
 jar {
diff --git a/tapestry-rest-jackson/build.gradle b/tapestry-rest-jackson/build.gradle
index aa806f3a4..6385f3fb6 100644
--- a/tapestry-rest-jackson/build.gradle
+++ b/tapestry-rest-jackson/build.gradle
@@ -4,7 +4,7 @@ dependencies {
     implementation project(':tapestry-core')
     implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
     implementation "com.github.victools:jsonschema-generator:${versions.jsonschemaGenerator}"
-    provided "javax.servlet:javax.servlet-api:${versions.servletapi}"
+    provided "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
 }
 
 jar {
diff --git a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonHttpRequestBodyConverter.java b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonHttpRequestBodyConverter.java
index 874a22e47..6129933be 100644
--- a/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonHttpRequestBodyConverter.java
+++ b/tapestry-rest-jackson/src/main/java/org/apache/tapestry5/rest/jackson/internal/JacksonHttpRequestBodyConverter.java
@@ -14,7 +14,7 @@ package org.apache.tapestry5.rest.jackson.internal;
 import java.io.IOException;
 import java.util.Set;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.apache.tapestry5.commons.internal.util.TapestryException;
 import org.apache.tapestry5.http.services.HttpRequestBodyConverter;
diff --git a/tapestry-runner/src/main/java/org/apache/tapestry5/test/JettyRunner.java b/tapestry-runner/src/main/java/org/apache/tapestry5/test/JettyRunner.java
index f6da5112f..0a15cf63e 100644
--- a/tapestry-runner/src/main/java/org/apache/tapestry5/test/JettyRunner.java
+++ b/tapestry-runner/src/main/java/org/apache/tapestry5/test/JettyRunner.java
@@ -16,8 +16,8 @@ package org.apache.tapestry5.test;
 
 import org.apache.commons.cli.*;
 import org.apache.tapestry5.test.constants.TapestryRunnerConstants;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ssl.SslSocketConnector;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.*;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.webapp.WebAppContext;
 
@@ -67,15 +67,18 @@ public class JettyRunner implements ServletContainerRunner
 
         if (keystoreFile.exists())
         {
-            SslContextFactory sslContextFactory = new SslContextFactory();
-
+            SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
             sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
-
             sslContextFactory.setKeyStorePassword("tapestry");
-
             sslContextFactory.setKeyManagerPassword("tapestry");
 
-            SslSocketConnector sslConnector = new SslSocketConnector(sslContextFactory);
+            HttpConfiguration httpConfiguration = new HttpConfiguration();
+            httpConfiguration.setSecureScheme("https");
+            httpConfiguration.setSecurePort(sslPort);
+
+            ServerConnector sslConnector = new ServerConnector(getServer(),
+                    new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+                    new HttpConnectionFactory(httpConfiguration));
 
             sslConnector.setPort(sslPort);
 
diff --git a/tapestry-spring/build.gradle b/tapestry-spring/build.gradle
index 1b4bb0c34..44d646d20 100644
--- a/tapestry-spring/build.gradle
+++ b/tapestry-spring/build.gradle
@@ -1,10 +1,14 @@
 description = "Integration of Tapestry with the Spring Inversion Of Control Container"
 
+// Spring 6 requires Java 17
+sourceCompatibility = "17"
+targetCompatibility = "17"
+
 dependencies {
   implementation project(':tapestry-core')
-  implementation "org.springframework:spring-web:3.2.9.RELEASE"
+  implementation "org.springframework:spring-websocket:6.0.11"
 
-  provided "javax.servlet:javax.servlet-api:${versions.servletapi}"
+  provided "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
 
   testImplementation project(':tapestry-test')
 }
@@ -27,4 +31,4 @@ task runTestApp1(type:JavaExec) {
     main = 'org.apache.tapestry5.test.JettyRunner'
     args "-d", "src/test/webapp1", "-p", "8080"
     classpath += project.sourceSets.test.runtimeClasspath
-}
\ No newline at end of file
+}
diff --git a/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/CustomizingContextLoader.java b/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/CustomizingContextLoader.java
index 994fe9118..215180471 100644
--- a/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/CustomizingContextLoader.java
+++ b/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/CustomizingContextLoader.java
@@ -21,7 +21,7 @@ import org.springframework.context.ApplicationContextException;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
 import org.springframework.web.context.ContextLoader;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 public class CustomizingContextLoader extends ContextLoader
 {
diff --git a/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/SpringModuleDef.java b/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/SpringModuleDef.java
index ceaec3047..7ff696820 100644
--- a/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/SpringModuleDef.java
+++ b/tapestry-spring/src/main/java/org/apache/tapestry5/internal/spring/SpringModuleDef.java
@@ -41,7 +41,7 @@ import org.springframework.web.context.ConfigurableWebApplicationContext;
 import org.springframework.web.context.WebApplicationContext;
 import org.springframework.web.context.support.WebApplicationContextUtils;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
diff --git a/tapestry-spring/src/main/java/org/apache/tapestry5/spring/ApplicationContextCustomizer.java b/tapestry-spring/src/main/java/org/apache/tapestry5/spring/ApplicationContextCustomizer.java
index 5f643cbeb..b19ff541f 100644
--- a/tapestry-spring/src/main/java/org/apache/tapestry5/spring/ApplicationContextCustomizer.java
+++ b/tapestry-spring/src/main/java/org/apache/tapestry5/spring/ApplicationContextCustomizer.java
@@ -17,7 +17,7 @@ package org.apache.tapestry5.spring;
 import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 /**
  * A bridge from Spring's approach to customizing the application context, over to Tapestry's approach. This is a
diff --git a/tapestry-spring/src/main/java/org/apache/tapestry5/spring/TapestrySpringFilter.java b/tapestry-spring/src/main/java/org/apache/tapestry5/spring/TapestrySpringFilter.java
index b8aa2eba8..a2dc91886 100644
--- a/tapestry-spring/src/main/java/org/apache/tapestry5/spring/TapestrySpringFilter.java
+++ b/tapestry-spring/src/main/java/org/apache/tapestry5/spring/TapestrySpringFilter.java
@@ -18,7 +18,7 @@ import org.apache.tapestry5.http.TapestryFilter;
 import org.apache.tapestry5.internal.spring.SpringModuleDef;
 import org.apache.tapestry5.ioc.def.ModuleDef;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 /**
  * Add logic to setup for Spring integration at startup.  In 5.1, this means creating a Spring ApplicationContext, and
diff --git a/tapestry-spring/src/main/java/org/apache/tapestry5/spring/modules/SpringModule.java b/tapestry-spring/src/main/java/org/apache/tapestry5/spring/modules/SpringModule.java
index e288344a3..5097453d6 100644
--- a/tapestry-spring/src/main/java/org/apache/tapestry5/spring/modules/SpringModule.java
+++ b/tapestry-spring/src/main/java/org/apache/tapestry5/spring/modules/SpringModule.java
@@ -36,7 +36,7 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.core.SpringVersion;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 import java.util.List;
 
 /**
diff --git a/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/CustomizingContextLoaderTest.java b/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/CustomizingContextLoaderTest.java
index 3539ca630..59eb731a1 100644
--- a/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/CustomizingContextLoaderTest.java
+++ b/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/CustomizingContextLoaderTest.java
@@ -20,7 +20,7 @@ import org.springframework.web.context.ContextLoader;
 import org.springframework.web.context.support.XmlWebApplicationContext;
 import org.testng.annotations.Test;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 public class CustomizingContextLoaderTest extends SpringTestCase
 {
diff --git a/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/SpringModuleDefTest.java b/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/SpringModuleDefTest.java
index e3840783c..84f015eb7 100644
--- a/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/SpringModuleDefTest.java
+++ b/tapestry-spring/src/test/java/org/apache/tapestry5/internal/spring/SpringModuleDefTest.java
@@ -26,7 +26,7 @@ import org.springframework.web.context.ConfigurableWebApplicationContext;
 import org.springframework.web.context.WebApplicationContext;
 import org.testng.annotations.Test;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 public class SpringModuleDefTest extends SpringTestCase
 {
diff --git a/tapestry-spring/src/test/java/org/apache/tapestry5/spring/SpringTestCase.java b/tapestry-spring/src/test/java/org/apache/tapestry5/spring/SpringTestCase.java
index 041b1c504..12d8f52a4 100644
--- a/tapestry-spring/src/test/java/org/apache/tapestry5/spring/SpringTestCase.java
+++ b/tapestry-spring/src/test/java/org/apache/tapestry5/spring/SpringTestCase.java
@@ -16,7 +16,7 @@ package org.apache.tapestry5.spring;
 
 import org.apache.tapestry5.test.TapestryTestCase;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 /**
  * Base class for Spring Integration test cases.
diff --git a/tapestry-spring/src/test/java/org/apache/tapestry5/spring/TapestryApplicationContextTest.java b/tapestry-spring/src/test/java/org/apache/tapestry5/spring/TapestryApplicationContextTest.java
index 6a955f72d..97a0a2da6 100644
--- a/tapestry-spring/src/test/java/org/apache/tapestry5/spring/TapestryApplicationContextTest.java
+++ b/tapestry-spring/src/test/java/org/apache/tapestry5/spring/TapestryApplicationContextTest.java
@@ -17,7 +17,7 @@ package org.apache.tapestry5.spring;
 import org.apache.tapestry5.http.TapestryFilter;
 import org.testng.annotations.Test;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 public class TapestryApplicationContextTest extends SpringTestCase
 {
diff --git a/tapestry-spring/src/test/java/org/apache/tapestry5/spring/integration/standard/TapestrySpringIntegrationTest.java b/tapestry-spring/src/test/java/org/apache/tapestry5/spring/integration/standard/TapestrySpringIntegrationTest.java
index 9d025e180..4077f9e1a 100644
--- a/tapestry-spring/src/test/java/org/apache/tapestry5/spring/integration/standard/TapestrySpringIntegrationTest.java
+++ b/tapestry-spring/src/test/java/org/apache/tapestry5/spring/integration/standard/TapestrySpringIntegrationTest.java
@@ -48,7 +48,7 @@ public class TapestrySpringIntegrationTest extends SeleniumTestCase
     {
         openBaseURL();
 
-        assertText("message", "SPRING VERSION 3.2.9.RELEASE: PIPELINE WAS INVOKED");
+        assertText("message", "SPRING VERSION 6.0.11: PIPELINE WAS INVOKED");
     }
 
     @Test
diff --git a/tapestry-spring/src/test/java/org/example/testapp/services/AppModule.java b/tapestry-spring/src/test/java/org/example/testapp/services/AppModule.java
index 216d0cdf0..07fe59e43 100644
--- a/tapestry-spring/src/test/java/org/example/testapp/services/AppModule.java
+++ b/tapestry-spring/src/test/java/org/example/testapp/services/AppModule.java
@@ -21,7 +21,7 @@ import org.apache.tapestry5.spring.ApplicationContextCustomizer;
 import org.apache.tapestry5.spring.modules.SpringModule;
 import org.springframework.web.context.ConfigurableWebApplicationContext;
 
-import javax.servlet.ServletContext;
+import jakarta.servlet.ServletContext;
 
 @ImportModule(SpringModule.class)
 public class AppModule
diff --git a/tapestry-upload/build.gradle b/tapestry-upload/build.gradle
index 7026fabd9..19abdd57d 100644
--- a/tapestry-upload/build.gradle
+++ b/tapestry-upload/build.gradle
@@ -2,9 +2,9 @@ description = "File Upload component, with supporting services"
 
 dependencies {
   implementation project(':tapestry-core')
-  api "commons-fileupload:commons-fileupload:1.3.2"
-  implementation "commons-io:commons-io:2.4"
-  provided "javax.servlet:javax.servlet-api:${versions.servletapi}"
+  api "org.apache.commons:commons-fileupload2-jakarta:2.0.0-M1"
+  implementation "commons-io:commons-io:2.13.0"
+  provided "jakarta.servlet:jakarta.servlet-api:${versions.servletapi}"
 
   testImplementation project(':tapestry-test')
 }
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImpl.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImpl.java
index f8315d4d6..826444877 100755
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImpl.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImpl.java
@@ -14,10 +14,10 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileItemFactory;
-import org.apache.commons.fileupload.FileUploadException;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload2.core.FileItem;
+import org.apache.commons.fileupload2.core.FileItemFactory;
+import org.apache.commons.fileupload2.core.FileUploadException;
+import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload;
 import org.apache.tapestry5.commons.util.CollectionFactory;
 import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
 import org.apache.tapestry5.ioc.annotations.Symbol;
@@ -26,8 +26,11 @@ import org.apache.tapestry5.upload.services.MultipartDecoder;
 import org.apache.tapestry5.upload.services.UploadSymbols;
 import org.apache.tapestry5.upload.services.UploadedFile;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
+
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -112,9 +115,9 @@ public class MultipartDecoderImpl implements MultipartDecoder, ThreadCleanupList
         }
     }
 
-    protected ServletFileUpload createFileUpload()
+    protected JakartaServletFileUpload createFileUpload()
     {
-        ServletFileUpload upload = new ServletFileUpload(fileItemFactory);
+        JakartaServletFileUpload upload = new JakartaServletFileUpload(fileItemFactory);
 
         // set maximum file upload size
         upload.setSizeMax(maxRequestSize);
@@ -147,9 +150,8 @@ public class MultipartDecoderImpl implements MultipartDecoder, ThreadCleanupList
 
                 try
                 {
-
-                    fieldValue = item.getString(requestEncoding);
-                } catch (UnsupportedEncodingException ex)
+                    fieldValue = item.getString(Charset.forName(requestEncoding));
+                } catch (IOException ex)
                 {
                     throw new RuntimeException(ex);
                 }
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilter.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilter.java
index bf92eeba1..3d1e46b1c 100755
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilter.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilter.java
@@ -14,13 +14,13 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload;
 import org.apache.tapestry5.http.services.HttpServletRequestFilter;
 import org.apache.tapestry5.http.services.HttpServletRequestHandler;
 import org.apache.tapestry5.upload.services.MultipartDecoder;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
 /**
@@ -39,7 +39,7 @@ public class MultipartServletRequestFilter implements HttpServletRequestFilter
     public boolean service(HttpServletRequest request, HttpServletResponse response, HttpServletRequestHandler handler)
             throws IOException
     {
-        HttpServletRequest newRequest = ServletFileUpload.isMultipartContent(request) ? decoder.decode(
+        HttpServletRequest newRequest = JakartaServletFileUpload.isMultipartContent(request) ? decoder.decode(
                 request) : request;
 
         return handler.service(newRequest, response);
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapper.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapper.java
index 4a732da35..8ebf997ee 100755
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapper.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapper.java
@@ -14,8 +14,8 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
 
 import static org.apache.tapestry5.commons.util.CollectionFactory.newMap;
 
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadExceptionFilter.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadExceptionFilter.java
index b92a66eb4..773180c73 100644
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadExceptionFilter.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadExceptionFilter.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload2.core.FileUploadException;
 import org.apache.tapestry5.annotations.Events;
 import org.apache.tapestry5.beanmodel.services.*;
 import org.apache.tapestry5.internal.services.ComponentResultProcessorWrapper;
@@ -32,8 +32,8 @@ import org.apache.tapestry5.upload.services.UploadEvents;
 import java.io.IOException;
 
 /**
- * Determines if there was an {@link org.apache.commons.fileupload.FileUploadException} processing the request and, if
- * so, triggers an exception event on the page. If the page fails to respond to the event, then
+ * Determines if there was an {@link org.apache.commons.fileupload2.core.FileUploadException} processing the request
+ * and, if so, triggers an exception event on the page. If the page fails to respond to the event, then
  */
 @Events(UploadEvents.UPLOAD_EXCEPTION + " when a exception occur processing the upload")
 public class UploadExceptionFilter implements ComponentEventRequestFilter
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadedFileItem.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadedFileItem.java
index d3c29ba8c..be481fd8e 100755
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadedFileItem.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/internal/services/UploadedFileItem.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload2.core.FileItem;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.tapestry5.upload.services.UploadedFile;
 
@@ -82,7 +82,7 @@ public class UploadedFileItem implements UploadedFile
     {
         try
         {
-            item.write(file);
+            item.write(file.toPath());
         }
         catch (Exception e)
         {
@@ -92,6 +92,13 @@ public class UploadedFileItem implements UploadedFile
 
     public void cleanup()
     {
-        item.delete();
+        try
+        {
+            item.delete();
+        }
+        catch (IOException e)
+        {
+            // ignore
+        }
     }
 }
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/modules/UploadModule.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/modules/UploadModule.java
index 3408c0c8e..45bd47a90 100755
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/modules/UploadModule.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/modules/UploadModule.java
@@ -14,8 +14,8 @@
 
 package org.apache.tapestry5.upload.modules;
 
-import org.apache.commons.fileupload.FileItemFactory;
-import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload2.core.DiskFileItemFactory;
+import org.apache.commons.fileupload2.core.FileItemFactory;
 import org.apache.commons.io.FileCleaner;
 import org.apache.tapestry5.commons.Configuration;
 import org.apache.tapestry5.commons.MappedConfiguration;
@@ -101,7 +101,7 @@ public class UploadModule
 
     /**
      * The default FileItemFactory used by the MultipartDecoder is
-     * {@link org.apache.commons.fileupload.disk.DiskFileItemFactory}.
+     * {@link org.apache.commons.fileupload2.core.DiskFileItemFactory}.
      */
     public static FileItemFactory buildDefaultFileItemFactory(@Symbol(UploadSymbols.REPOSITORY_THRESHOLD)
                                                               int repositoryThreshold,
@@ -109,13 +109,12 @@ public class UploadModule
                                                               @Symbol(UploadSymbols.REPOSITORY_LOCATION)
                                                               String repositoryLocation)
     {
-        return new DiskFileItemFactory(repositoryThreshold, new File(repositoryLocation));
+        return DiskFileItemFactory.builder().setFile(repositoryLocation).setBufferSize(repositoryThreshold).get();
     }
 
     public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
     {
-        configuration.add(UploadSymbols.REPOSITORY_THRESHOLD, Integer
-                .toString(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD));
+        configuration.add(UploadSymbols.REPOSITORY_THRESHOLD, Integer.toString(DiskFileItemFactory.DEFAULT_THRESHOLD));
         configuration.add(UploadSymbols.REPOSITORY_LOCATION, System.getProperty("java.io.tmpdir"));
         configuration.add(UploadSymbols.REQUESTSIZE_MAX, NO_LIMIT);
         configuration.add(UploadSymbols.FILESIZE_MAX, NO_LIMIT);
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/MultipartDecoder.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/MultipartDecoder.java
index b26bf4200..75a130b94 100755
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/MultipartDecoder.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/MultipartDecoder.java
@@ -14,9 +14,8 @@
 
 package org.apache.tapestry5.upload.services;
 
-import org.apache.commons.fileupload.FileUploadException;
-
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
+import org.apache.commons.fileupload2.core.FileUploadException;
 
 /**
  * Responsible for detecting and processing file upload requests, using Jakarta Commons FileUpload. Implementations of
diff --git a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/UploadEvents.java b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/UploadEvents.java
index 9e9349877..6f34a82c5 100644
--- a/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/UploadEvents.java
+++ b/tapestry-upload/src/main/java/org/apache/tapestry5/upload/services/UploadEvents.java
@@ -21,7 +21,7 @@ public class UploadEvents
 {
     /**
      * Name of event fired on a page when an upload form associated with that page encounters a {@link
-     * org.apache.commons.fileupload.FileUploadException} while processing the multipart form submission.
+     * org.apache.commons.fileupload2.core.FileUploadException} while processing the multipart form submission.
      */
     public static final String UPLOAD_EXCEPTION = "uploadException";
-}                       
\ No newline at end of file
+}
diff --git a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImplTest.java b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImplTest.java
index 5cbf4d1c2..0bd6ceaf1 100755
--- a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImplTest.java
+++ b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartDecoderImplTest.java
@@ -14,14 +14,14 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileItemFactory;
-import org.apache.commons.fileupload.disk.DiskFileItemFactory;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload2.core.DiskFileItemFactory;
+import org.apache.commons.fileupload2.core.FileItem;
+import org.apache.commons.fileupload2.core.FileItemFactory;
+import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload;
 import org.apache.tapestry5.test.TapestryTestCase;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -30,7 +30,8 @@ import java.util.List;
 
 public class MultipartDecoderImplTest extends TapestryTestCase
 {
-    private final FileItemFactory fileItemFactory = new DiskFileItemFactory(888, new File("/tmp"));
+    private final FileItemFactory fileItemFactory = DiskFileItemFactory.builder()
+            .setBufferSize(888).setFile(new File("/tmp")).get();
 
     private static final String CHARSET = "UTF-8";
 
@@ -41,7 +42,7 @@ public class MultipartDecoderImplTest extends TapestryTestCase
 
         replay();
 
-        ServletFileUpload servletFileUpload = decoder.createFileUpload();
+        JakartaServletFileUpload servletFileUpload = decoder.createFileUpload();
         assertNotNull(servletFileUpload);
 
         verify();
diff --git a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilterTest.java b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilterTest.java
index 8b71963fe..b2b541f5d 100755
--- a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilterTest.java
+++ b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/MultipartServletRequestFilterTest.java
@@ -19,8 +19,8 @@ import org.apache.tapestry5.test.TapestryTestCase;
 import org.apache.tapestry5.upload.services.MultipartDecoder;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 
 public class MultipartServletRequestFilterTest extends TapestryTestCase
 {
diff --git a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapperTest.java b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapperTest.java
index 8f7cdb38f..3eaf07ae3 100755
--- a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapperTest.java
+++ b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/ParametersServletRequestWrapperTest.java
@@ -18,7 +18,7 @@ import static org.easymock.EasyMock.*;
 import static org.testng.Assert.*;
 import org.testng.annotations.Test;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Map;
diff --git a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/StubFileItem.java b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/StubFileItem.java
index 49dbe1182..315ebcdc2 100755
--- a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/StubFileItem.java
+++ b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/StubFileItem.java
@@ -14,10 +14,12 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileItemHeaders;
+import org.apache.commons.fileupload2.core.FileItem;
+import org.apache.commons.fileupload2.core.FileItemHeaders;
 
 import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.file.Path;
 
 public class StubFileItem implements FileItem
 {
@@ -43,7 +45,7 @@ public class StubFileItem implements FileItem
     }
 
     @Override
-    public InputStream getInputStream() throws IOException
+    public InputStream getInputStream()
     {
         return null;
     }
@@ -80,7 +82,7 @@ public class StubFileItem implements FileItem
     }
 
     @Override
-    public String getString(String string) throws UnsupportedEncodingException
+    public String getString(Charset charset)
     {
         return getString();
     }
@@ -92,14 +94,16 @@ public class StubFileItem implements FileItem
     }
 
     @Override
-    public void write(File file) throws Exception
+    public FileItem write(Path file)
     {
+        return this;
     }
 
     @Override
-    public void delete()
+    public FileItem delete()
     {
         isDeleted = true;
+        return this;
     }
 
     @Override
@@ -110,9 +114,10 @@ public class StubFileItem implements FileItem
     }
 
     @Override
-    public void setFieldName(String fieldName)
+    public FileItem setFieldName(String fieldName)
     {
         this.fieldName = fieldName;
+        return this;
     }
 
     @Override
@@ -122,9 +127,10 @@ public class StubFileItem implements FileItem
     }
 
     @Override
-    public void setFormField(boolean formField)
+    public FileItem setFormField(boolean formField)
     {
         this.formField = formField;
+        return this;
     }
 
     @Override
@@ -157,7 +163,8 @@ public class StubFileItem implements FileItem
 
     /* unused method but required by FileItem interface */
     @Override
-    public void setHeaders(FileItemHeaders headers)
+    public FileItem setHeaders(FileItemHeaders headers)
     {
+        return this;
     }
 }
diff --git a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/UploadedFileItemTest.java b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/UploadedFileItemTest.java
index 8786f3774..ec0256ff3 100755
--- a/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/UploadedFileItemTest.java
+++ b/tapestry-upload/src/test/java/org/apache/tapestry5/upload/internal/services/UploadedFileItemTest.java
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry5.upload.internal.services;
 
-import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload2.core.FileItem;
 import org.apache.commons.io.input.NullInputStream;
 import org.apache.tapestry5.test.TapestryTestCase;
 import org.testng.annotations.Test;
@@ -122,7 +122,7 @@ public class UploadedFileItemTest extends TapestryTestCase
         File out = new File("");
         UploadedFileItem uploadedFile = new UploadedFileItem(item);
 
-        item.write(out);
+        expect(item.write(out.toPath())).andReturn(item);
 
         replay();
 
@@ -137,7 +137,7 @@ public class UploadedFileItemTest extends TapestryTestCase
     {
         FileItem item = newMock(FileItem.class);
         UploadedFileItem uploadedFile = new UploadedFileItem(item);
-        item.delete();
+        expect(item.delete()).andReturn(item);
 
         replay();
         uploadedFile.cleanup();
diff --git a/tapestry-upload/src/test/java/org/example/upload/pages/Start.java b/tapestry-upload/src/test/java/org/example/upload/pages/Start.java
index 6de3c3b23..eb5439dc2 100755
--- a/tapestry-upload/src/test/java/org/example/upload/pages/Start.java
+++ b/tapestry-upload/src/test/java/org/example/upload/pages/Start.java
@@ -14,7 +14,7 @@
 
 package org.example.upload.pages;
 
-import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload2.core.FileUploadException;
 import org.apache.tapestry5.PersistenceConstants;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;