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

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

commit ed028908a91a6a8620582db3fdc1b3de1b8702f9
Author: Ben Weidig <[email protected]>
AuthorDate: Sat Aug 29 11:14:52 2026 +0200

    Add 5.9 version of the User Guide
    
    Branched from 5.8 and updated for the Jakarta EE 9 release:
    
    - antora.yml: version 5.9, add tapestry-version attribute
    - New page: Migrating to Jakarta EE (dual artifact builds, package
      renames, migration tool usage)
    - New page: Release Notes 5.9 (5.9.0 and 5.9.1)
    - Document Session.LockMode and containsAttribute in Session Storage
    - Document the Recursive/RecursiveBody components in Component Reference
    - Document the t5/core/ajax headers option in Ajax and Zones
    - Replace javax.* with jakarta.* in index, configuration, jpa,
      bean-validation, uploading-files and the JSR-330 page
    - Update the Spring integration page for Spring 6 / Java 17
---
 antora.yml                                         |   3 +-
 modules/ROOT/nav.adoc                              |   2 +
 modules/ROOT/pages/ajax-and-zones.adoc             |  19 ++
 modules/ROOT/pages/bean-validation.adoc            |   4 +-
 modules/ROOT/pages/component-reference.adoc        |   5 +
 modules/ROOT/pages/configuration.adoc              |  13 +-
 modules/ROOT/pages/index.adoc                      |   3 +-
 .../pages/integrating-with-spring-framework.adoc   |   9 +-
 modules/ROOT/pages/jakarta-ee-migration.adoc       | 242 +++++++++++++++++++++
 modules/ROOT/pages/jpa.adoc                        |  46 ++--
 modules/ROOT/pages/release-notes-5.9.adoc          |  86 ++++++++
 modules/ROOT/pages/session-storage.adoc            |  45 ++++
 modules/ROOT/pages/uploading-files.adoc            |  13 +-
 .../pages/using-jsr-330-standard-annotations.adoc  |  19 +-
 14 files changed, 462 insertions(+), 47 deletions(-)

diff --git a/antora.yml b/antora.yml
index 82316c5..06d455e 100644
--- a/antora.yml
+++ b/antora.yml
@@ -1,6 +1,6 @@
 name: userguide
 title: User Guide
-version: 5.8
+version: 5.9
 nav:
 - modules/ROOT/nav.adoc
 - modules/cookbook/nav.adoc
@@ -8,4 +8,5 @@ nav:
 asciidoc:
   attributes:
     javadoc-version: current
+    tapestry-version: 5.9.1
     tapestry-namespace: http://tapestry.apache.org/schema/tapestry_5_4.xsd
diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc
index fcc0eee..f49113c 100644
--- a/modules/ROOT/nav.adoc
+++ b/modules/ROOT/nav.adoc
@@ -3,6 +3,7 @@
 .Tapestry Core
 * Basics
 // ** xref:supported-environments-and-versions.adoc[]
+** xref:jakarta-ee-migration.adoc[]
 ** xref:project-layout.adoc[]
 ** xref:configuration.adoc[]
 ** xref:runtime-exceptions.adoc[]
@@ -100,6 +101,7 @@
 * xref:third-party-modules.adoc[]
 
 .Release Notes
+* xref:release-notes-5.9.adoc[]
 * xref:release-notes-5.8.adoc[]
 * xref:release-notes-5.7.adoc[]
 * xref:release-notes-5.6.adoc[]
diff --git a/modules/ROOT/pages/ajax-and-zones.adoc 
b/modules/ROOT/pages/ajax-and-zones.adoc
index fc486f8..eb76e08 100644
--- a/modules/ROOT/pages/ajax-and-zones.adoc
+++ b/modules/ROOT/pages/ajax-and-zones.adoc
@@ -387,3 +387,22 @@ require(["t5/core/ajax", "jquery"], function (ajax, $) {
 
 If you're trying to invoke a page class event handler, you can change line 5 
above to element: `null`.
 You do need to explicitly set the element property, otherwise the ajax 
function will treat the first parameter, url, as an URL and not as an event 
name.
+
+=== Custom request headers
+
+Since Tapestry 5.9 (issue:2782[]), the `options` parameter accepts a `headers` 
object whose key/value pairs are added to the request headers:
+
+[source,javascript]
+----
+require(["t5/core/ajax"], function (ajax) {
+    ajax('answer', {
+        element: null,
+        headers: {
+            "X-Tenant-Id": currentTenantId
+        },
+        success: function(response) { /* ... */ }
+    });
+});
+----
+
+This is useful for passing correlation ids, tenant identifiers or tokens that 
a server-side filter reads from the request, without encoding them into the URL 
or the form data.
diff --git a/modules/ROOT/pages/bean-validation.adoc 
b/modules/ROOT/pages/bean-validation.adoc
index 0488fd4..2094a00 100644
--- a/modules/ROOT/pages/bean-validation.adoc
+++ b/modules/ROOT/pages/bean-validation.adoc
@@ -15,7 +15,7 @@ In order to use this library you have to choose an 
implementation of the JSR-303
 This library is not specific to any implementation of JSR-303 and will work 
with any implementation of your choice.
 
 === Bootstraping the Bean Validator
-The javadoc:org.apache.tapestry5.beanvalidator.BeanValidatorSource[] service 
is responsible for bootstrapping the `javax.validation.Validator`.
+The javadoc:org.apache.tapestry5.beanvalidator.BeanValidatorSource[] service 
is responsible for bootstrapping the `jakarta.validation.Validator`.
 You can contribute a 
javadoc:org.apache.tapestry5.beanvalidator.BeanValidatorConfigurer[] to the 
configuration of this service in order to participate on the configuration of 
`Validator`.
 
 .Module Class
@@ -26,7 +26,7 @@ public static void 
provideBeanValidatorConfigurer(OrderedConfiguration<BeanValid
 {
    configuration.add("MyConfigurer", new BeanValidatorConfigurer()
    {
-      public void configure(javax.validation.Configuration<?> configuration)
+      public void configure(jakarta.validation.Configuration<?> configuration)
       {
          configuration.ignoreXmlConfiguration();
       }
diff --git a/modules/ROOT/pages/component-reference.adoc 
b/modules/ROOT/pages/component-reference.adoc
index ec82000..e472aab 100644
--- a/modules/ROOT/pages/component-reference.adoc
+++ b/modules/ROOT/pages/component-reference.adoc
@@ -57,6 +57,11 @@ Conditionally renders its body. May render its tag and any 
informal parameters.
 javadoc:org.apache.tapestry5.corelib.components.Loop[]::
 Loops over a number of items (provided by its source parameter), rendering its 
body for each one.
 
+javadoc:org.apache.tapestry5.corelib.components.Recursive[]::
+Loop-like component that renders its body recursively over parent/child data 
such as trees or nested menus.
+The point at which the children are rendered is marked with 
javadoc:org.apache.tapestry5.corelib.components.RecursiveBody[].
+Since 5.9.
+
 javadoc:org.apache.tapestry5.corelib.components.Unless[]::
 Inverse of the javadoc:org.apache.tapestry5.corelib.components.If[] component, 
renders its body if the condition is `false`.
 
diff --git a/modules/ROOT/pages/configuration.adoc 
b/modules/ROOT/pages/configuration.adoc
index baabbd8..a24160c 100644
--- a/modules/ROOT/pages/configuration.adoc
+++ b/modules/ROOT/pages/configuration.adoc
@@ -12,10 +12,11 @@ Most of the configuration is boilerplate, nearly the same 
for all applications.
 .web.xml (partial)
 [source,xml]
 ----
-<!DOCTYPE web-app
-      PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
-      "http://java.sun.com/dtd/web-app_2_3.dtd";>
-<web-app>
+<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee";
+         xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
+         https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd";
+         version="5.0">
     <display-name>My Tapestry Application</display-name>
     <context-param>
         <param-name>tapestry.app-package</param-name>
@@ -33,6 +34,10 @@ Most of the configuration is boilerplate, nearly the same 
for all applications.
 ----
 <1> Or org.apache.tapestry5.*http*.TapestryFilter if you're using 
tapestry-http without tapestry-core
 
+NOTE: The descriptor above targets Jakarta EE 9, which is what Tapestry 5.9 is 
built on.
+If you are using the javax build, keep the `javaee` namespace (or the older 
DTD) and a `version` of 3.1 or below.
+See xref:jakarta-ee-migration.adoc[] for the difference between the two builds.
+
 The application-specific part, the `tapestry.app-package` context parameter, 
provides your application's root package name.
 Tapestry uses this to locate your page and component classes.
 It expects page classes in the pages sub-package and components in the 
components sub-package.
diff --git a/modules/ROOT/pages/index.adoc b/modules/ROOT/pages/index.adoc
index 80fb94d..3062afe 100644
--- a/modules/ROOT/pages/index.adoc
+++ b/modules/ROOT/pages/index.adoc
@@ -40,7 +40,8 @@ Once you work in Tapestry there's no going back!
 Tapestry is released under the Apache Software License 2.0.
 
 == Tapestry as a Java Servlet Filter
-Tapestry integrates with the Java Servlet API by implementing 
https://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html[javax.servlet.Filter].
+Tapestry integrates with the Jakarta Servlet API by implementing 
https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/filter[jakarta.servlet.Filter].
+If you are using the javax build of Tapestry 5.9, it implements 
`javax.servlet.Filter` instead; see xref:jakarta-ee-migration.adoc[].
 Similar to servlets, filters are added to a web application in the deployment 
descriptor (`web.xml`).
 
 .web.xml (partial)
diff --git a/modules/ROOT/pages/integrating-with-spring-framework.adoc 
b/modules/ROOT/pages/integrating-with-spring-framework.adoc
index 8b0bd75..c446ac2 100644
--- a/modules/ROOT/pages/integrating-with-spring-framework.adoc
+++ b/modules/ROOT/pages/integrating-with-spring-framework.adoc
@@ -6,8 +6,13 @@ You can also go the other way, injecting Tapestry services in 
Spring beans.
 For integrating Spring Security into your application, see 
xref:security.adoc[].
 
 == Spring Version
-This module is compiled and tested against Spring Framework 2.5.6.
-It should be reasonable to override the dependency to earlier versions of 
Spring, though the code makes use of some APIs that were added to Spring to 
support JDK 1.5 annotations.
+As of Tapestry 5.9 this module is compiled and tested against Spring Framework 
6.0.11.
+
+[IMPORTANT]
+====
+Spring 6 requires *Java 17*.
+The rest of Tapestry targets Java 8 bytecode and is tested on Java 11 and 17, 
so adding `tapestry-spring` raises the minimum Java version for your whole 
application to 17.
+====
 
 == Usage
 The integration is designed to be a very thin layer on top of Spring's normal 
configuration for a web application.
diff --git a/modules/ROOT/pages/jakarta-ee-migration.adoc 
b/modules/ROOT/pages/jakarta-ee-migration.adoc
new file mode 100644
index 0000000..3659d11
--- /dev/null
+++ b/modules/ROOT/pages/jakarta-ee-migration.adoc
@@ -0,0 +1,242 @@
+= Migrating to Jakarta EE
+:navtitle: Jakarta EE Migration
+
+Tapestry 5.9 is the first release built on https://jakarta.ee/[Jakarta EE] 9.
+Everything that used to live under `javax.servlet`, `javax.persistence`, 
`javax.validation` and `javax.inject` now lives under `jakarta.*`.
+
+This page explains what changed, how to pick the right artifacts, and what to 
do in your own code.
+
+== Two builds of the same release
+
+The move to Jakarta EE 9 cannot be made backwards compatible: a class compiled 
against `javax.servlet.http.HttpServletRequest` will not run in a container 
that only provides `jakarta.servlet.http.HttpServletRequest`, and vice versa.
+
+Rather than force every application onto Jakarta at once, Tapestry 5.9 is 
released from two branches at the same version number:
+
+[%autowidth]
+|===
+|Build |Servlet API |Artifact ids |Typical container
+
+|Jakarta
+|`jakarta.servlet` 5.0
+|suffixed with `-jakarta`, e.g. `tapestry-core-jakarta`
+|Tomcat 10+, Jetty 11+, WildFly 27+
+
+|javax
+|`javax.servlet` 3.0+
+|unchanged, e.g. `tapestry-core`
+|Tomcat 9, Jetty 9-10
+|===
+
+Both builds contain the same features and bug fixes.
+The javax build exists so that applications which cannot move their container 
yet can still upgrade to 5.9.
+
+[IMPORTANT]
+====
+Do not mix the two.
+A project must use either all `-jakarta` artifacts or all unsuffixed ones, 
together with a matching Servlet API dependency and container.
+====
+
+=== Artifacts that carry the suffix
+
+These modules touch a Jakarta EE API and therefore ship twice:
+
+[cols="1,1,1",frame=none,grid=none]
+|===
+a|
+* `tapestry-core`
+* `tapestry-http`
+* `tapestry-ioc`
+* `tapestry-ioc-jcache`
+* `tapestry-ioc-junit`
+* `tapestry-upload`
+* `tapestry-webresources`
+a|
+* `tapestry-hibernate`
+* `tapestry-hibernate-core`
+* `tapestry-jpa`
+* `tapestry-beanvalidator`
+* `tapestry-spring`
+* `tapestry-mongodb`
+* `tapestry-jmx`
+a|
+* `tapestry-rest-jackson`
+* `tapestry-openapi-viewer`
+* `tapestry-kaptcha`
+* `tapestry-clojure`
+* `tapestry-test`
+* `tapestry-runner`
+* `tapestry-spock`
+|===
+
+=== Artifacts that do not
+
+These modules do not depend on any Jakarta EE API, so a single artifact serves 
both builds and the id never changes:
+
+`plastic`, `commons`, `beanmodel`, `tapestry-json`, `tapestry-func`, 
`tapestry5-annotations`, `genericsresolver-guava`, `tapestry-javadoc`, 
`tapestry-version-migrator`
+
+== Updating your build
+
+.Maven: a Jakarta project
+[source,xml,subs="+attributes"]
+----
+<properties>
+  <!-- Tapestry artifact id suffix: empty value or '-jakarta' -->
+  <tapestry-artifact-suffix>-jakarta</tapestry-artifact-suffix>
+</properties>
+
+<dependency>
+  <groupId>org.apache.tapestry</groupId>
+  <artifactId>tapestry-core${tapestry-artifact-suffix}</artifactId>
+  <version>{tapestry-version}</version>
+</dependency>
+
+<dependency>
+  <groupId>jakarta.servlet</groupId>
+  <artifactId>jakarta.servlet-api</artifactId>
+  <version>5.0.0</version>
+  <scope>provided</scope>
+</dependency>
+----
+
+Driving the suffix from a property means you can switch a project between the 
two builds by editing a single value.
+
+.Gradle: a Jakarta project
+[source,gradle,subs="+attributes"]
+----
+implementation 'org.apache.tapestry:tapestry-core-jakarta:{tapestry-version}'
+compileOnly 'jakarta.servlet:jakarta.servlet-api:5.0.0'
+----
+
+=== Letting the migration tool do it
+
+The `tapestry-version-migrator` module can introduce the property and rewrite 
the artifact ids across a multi-module Maven build for you.
+Run it from the root of your project:
+
+[subs="+attributes"]
+----
+java -jar tapestry-version-migrator-{tapestry-version}.jar artifactSuffix 
-jakarta
+----
+
+It walks the current directory recursively, and for every `pom.xml` it finds 
it adds a `tapestry-artifact-suffix` property and appends 
`${tapestry-artifact-suffix}` to the artifact id of every Tapestry dependency 
that has a suffixed variant.
+Passing no suffix at all switches a project back to the javax artifacts:
+
+[subs="+attributes"]
+----
+java -jar tapestry-version-migrator-{tapestry-version}.jar artifactSuffix
+----
+
+NOTE: The tool rewrites `pom.xml` files in place through an XML transformer, 
so it will also reformat them. Run it on a clean working tree so you can review 
the diff.
+
+== Updating your code
+
+=== Package renames
+
+Change the imports in your own classes:
+
+[%autowidth]
+|===
+|Before |After
+
+|`javax.servlet.*`
+|`jakarta.servlet.*`
+
+|`javax.servlet.http.*`
+|`jakarta.servlet.http.*`
+
+|`javax.persistence.*`
+|`jakarta.persistence.*`
+
+|`javax.validation.*`
+|`jakarta.validation.*`
+
+|`javax.inject.*`
+|`jakarta.inject.*`
+
+|`javax.annotation.*`
+|`jakarta.annotation.*`
+|===
+
+Tapestry's own packages are unchanged: 
javadoc:org.apache.tapestry5.TapestryFilter[] and everything else under 
`org.apache.tapestry5` keeps its name in both builds.
+
+=== web.xml
+
+The deployment descriptor namespace changes, but the Tapestry filter 
declaration itself does not:
+
+.web.xml (partial)
+[source,xml]
+----
+<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee";
+         xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
+         https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd";
+         version="5.0">
+...
+    <filter>
+        <filter-name>app</filter-name>
+        <filter-class>org.apache.tapestry5.TapestryFilter</filter-class>
+    </filter>
+...
+</web-app>
+----
+
+See xref:configuration.adoc[] for the rest of the descriptor.
+
+=== File uploads
+
+`tapestry-upload` moved from Apache Commons FileUpload 1.x to 
https://commons.apache.org/proper/commons-fileupload/[Commons FileUpload 2] 
(`commons-fileupload2-jakarta`).
+Two things are visible to applications:
+
+* `org.apache.commons.fileupload.FileUploadException` is now 
`org.apache.commons.fileupload2.core.FileUploadException`.
+Adjust any exception handler that catches it by name.
+* The protected `MultipartDecoderImpl.createFileUpload()` method now returns 
`JakartaServletFileUpload` instead of `ServletFileUpload`.
+If you subclass it to customise upload handling, update the signature.
+
+See xref:uploading-files.adoc[] for details.
+
+=== Third-party libraries
+
+Anything else in your dependency tree that implements or consumes a Servlet 
API interface has to move to a Jakarta-compatible version as well.
+The versions Tapestry 5.9 itself builds against are:
+
+[%autowidth]
+|===
+|Library |Version |Note
+
+|Jakarta Servlet
+|5.0.0
+|
+
+|Jakarta Persistence
+|3.0.0
+|EclipseLink 3.0.4 is used in the tests
+
+|Jakarta Validation
+|3.0.2
+|with Hibernate Validator 8.0.1.Final
+
+|Jakarta Inject
+|2.0.1
+|see xref:using-jsr-330-standard-annotations.adoc[]
+
+|Commons FileUpload
+|2.0.0-M1
+|`commons-fileupload2-jakarta`
+
+|Spring
+|6.0.11
+|requires Java 17, see below
+|===
+
+[IMPORTANT]
+====
+`tapestry-spring` depends on Spring 6, which requires *Java 17*.
+The rest of Tapestry targets Java 8 bytecode and is tested on Java 11 and 17.
+If your application uses `tapestry-spring`, Java 17 becomes the minimum for 
the whole project.
+====
+
+Note that `tapestry-hibernate` still builds against Hibernate 5.4, whose own 
upgrade was postponed because of the size of the API change between Hibernate 5 
and 6.
+
+== Staying on javax
+
+If you are not ready, you do not have to do anything: keep using the 
unsuffixed artifacts and upgrade the version to {tapestry-version} as usual.
+You get every 5.9 fix and feature, and can move to the `-jakarta` artifacts 
later.
diff --git a/modules/ROOT/pages/jpa.adoc b/modules/ROOT/pages/jpa.adoc
index feff5a6..da76b62 100644
--- a/modules/ROOT/pages/jpa.adoc
+++ b/modules/ROOT/pages/jpa.adoc
@@ -21,9 +21,9 @@ If you're using Maven, just add the `tapestry-jpa` dependency 
to your applicatio
 If you aren't using Maven (or Gradle, Ivy, etc), you'll have to download the 
jar and its dependencies yourself.
 
 == Selecting a JPA Implementation
-The `tapestry-jpa` module includes a dependency on a JPA specification (API) 
from Geronimo but not an implementation.
-You'll have to chose a JPA implementation, such as EclipseLink or Hibernate.
-The`tapestry-jpa` module assumes you'll use EclipseLink.
+The `tapestry-jpa` module depends on the Jakarta Persistence API 
(`jakarta.persistence:jakarta.persistence-api:3.0.0`) but not on an 
implementation.
+You'll have to choose a JPA implementation, such as EclipseLink or Hibernate.
+The `tapestry-jpa` module assumes you'll use EclipseLink.
 You just have to add the EclipseLink dependency:
 
 .pom.xml (partial) for EclipseLink
@@ -36,40 +36,28 @@ You just have to add the EclipseLink dependency:
 </dependency>
 ----
 
-Or, if you'd rather use Hibernate as your JPA implementation, you'll want to 
exclude either the Gernonimo or Hibernate JPA specification JAR:
+Whichever implementation you pick, it has to be a Jakarta Persistence 3.0 
build – EclipseLink 3.x or Hibernate 6.x – because a JPA 2.x implementation 
still expects `javax.persistence`.
+Tapestry's own integration tests run against EclipseLink 3.0.4.
 
-.pom.xml (partial) for Hibernate
-[source,xml]
-----
-<dependency>
-    <groupId>org.hibernate</groupId>
-    <artifactId>hibernate-entitymanager</artifactId>
-    <version>${hibernate-version}</version>
-    <exclusions>
-        <exclusion>
-            <!-- omit Geronimo JPA spec to avoid conflict with Hibernate JPA 
spec -->
-            <groupId>org.apache.geronimo.specs</groupId>
-            <artifactId>geronimo-jpa_2.0_spec</artifactId>
-        </exclusion>
-    </exclusions>
-</dependency>
-----
+IMPORTANT: This applies to the Jakarta build of Tapestry 5.9.
+If you are using the javax build, stay on a JPA 2.x implementation.
+See xref:jakarta-ee-migration.adoc[].
 
 == Configuring JPA
 The `persistence.xml` file is the standard configuration file in JPA used to 
define the persistence units.
-Tapestry reads this file to create the 
`javax.persistence.EntityManagerFactory`.
+Tapestry reads this file to create the 
`jakarta.persistence.EntityManagerFactory`.
 The following example demonstrates a `persistence.xml` file.
 
 .persistence.xml
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<persistence xmlns="http://java.sun.com/xml/ns/persistence"; version="2.0">
+<persistence xmlns="https://jakarta.ee/xml/ns/persistence"; version="3.0">
    <persistence-unit name="DemoUnit" transaction-type="RESOURCE_LOCAL">
        <properties>
-          <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" 
/>
-          <property name="javax.persistence.jdbc.url"    
value="jdbc:h2:mem:test" />
-          <property name="javax.persistence.jdbc.user"   value="sa" />
+          <property name="jakarta.persistence.jdbc.driver" 
value="org.h2.Driver" />
+          <property name="jakarta.persistence.jdbc.url"    
value="jdbc:h2:mem:test" />
+          <property name="jakarta.persistence.jdbc.user"   value="sa" />
           <property name="eclipselink.ddl-generation"    
value="create-tables"/>
           <property name="eclipselink.logging.level"     value="fine"/>
       </properties>
@@ -94,7 +82,7 @@ public static void provideFactoryDefaults(final 
MappedConfiguration<String, Stri
 
 == XML-less JPA configuration
 With Tapestry, configuring JPA can be much simpler than described by the JPA 
specification.
-Tapestry allows you to configure the `javax.persistence.EntityManagerFactory` 
programmatically, without writing any XML.
+Tapestry allows you to configure the 
`jakarta.persistence.EntityManagerFactory` programmatically, without writing 
any XML.
 For example, imagine that you want to use JDBC connections managed by the 
container and provided through JNDI.
 The resulting persistence descriptor might look like this:
 
@@ -102,8 +90,8 @@ The resulting persistence descriptor might look like this:
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<persistence xmlns="http://java.sun.com/xml/ns/persistence";
-             version="2.0">
+<persistence xmlns="https://jakarta.ee/xml/ns/persistence";
+             version="3.0">
    <persistence-unit name="JTAUnit" transaction-type="RESOURCE_LOCAL">
       <non-jta-data-source>jdbc/JPATest</non-jta-data-source>
       <properties>
@@ -138,7 +126,7 @@ In the example above you can see a contribution to the 
javadoc:org.apache.tapest
 This service is responsible for creating the `EntityManagerFactory` to be used 
to create `EntityManager`.
 When the service is initialized, it parses the `persistence.xml` file, if 
available.
 For any persistence unit defined in the XML descriptor a 
javadoc:org.apache.tapestry5.jpa.TapestryPersistenceUnitInfo[] object is 
created.
-The `TapestryPersistenceUnitInfo` interface is a mutable extension of the 
`javax.persistence.spi.PersistenceUnitInfo` interface (defined in the JPA 
specification) that allows you to configure a persistence unit programmatically.
+The `TapestryPersistenceUnitInfo` interface is a mutable extension of the 
`jakarta.persistence.spi.PersistenceUnitInfo` interface (defined in the JPA 
specification) that allows you to configure a persistence unit programmatically.
 
 After parsing the persistence descriptor, the `EntityManagerSource` service 
applies its configuration to create further persistence units and/or update the 
existing ones.
 The service’s configuration is a map in which persistence unit names are 
associated with javadoc:org.apache.tapestry5.jpa.PersistenceUnitConfigurer[] 
instances.
diff --git a/modules/ROOT/pages/release-notes-5.9.adoc 
b/modules/ROOT/pages/release-notes-5.9.adoc
new file mode 100644
index 0000000..e3e4a13
--- /dev/null
+++ b/modules/ROOT/pages/release-notes-5.9.adoc
@@ -0,0 +1,86 @@
+= Tapestry 5.9
+
+Tapestry 5.9 moves the framework to Jakarta EE 9.
+Every module that touches the Servlet, Persistence, Validation or CDI APIs now 
compiles against the `jakarta.*` packages instead of `javax.*`.
+
+Because that is a breaking change for existing applications, 5.9 is released 
twice from two branches:
+the Jakarta build publishes artifacts with a `-jakarta` suffix, and the javax 
build keeps the historical, unsuffixed artifact names.
+Both carry the same version number and the same features.
+See xref:jakarta-ee-migration.adoc[] for what this means for your project and 
how to move over.
+
+== Tapestry 5.9.1
+Tapestry 5.9.1 is a bugfix release and a drop-in replacement for Tapestry 
5.9.0.
+
+=== Improvements Made
+* issue:2814[] - Upgrade ASM to 9.9
+* issue:2758[] - Constructor/Builder injection should support 
javadoc:org.apache.tapestry5.ioc.annotations.Symbol[label=@Symbol] for 
`List`/`Collection`/`Map` even without 
javadoc:org.apache.tapestry5.ioc.annotations.Inject[label=@Inject]
+
+=== Bugs Fixed
+* issue:2813[] - NPE when 
javadoc:org.apache.tapestry5.annotations.Cached[label=@Cached] applied to 
method returning a generic type in multiple classloader mode
+
+
+== Tapestry 5.9.0
+Tapestry 5.9.0 is a major release built on Jakarta EE 9.
+
+Highlights:
+
+* issue:2741[] and issue:2788[] - *Jakarta EE 9 support.*
+Tapestry now targets Jakarta Servlet 5.0, Jakarta Persistence 3.0, Jakarta 
Validation 3.0 and Jakarta Inject 2.0.
+The corresponding `javax.*` imports in your own code have to be changed to 
`jakarta.*`.
+* issue:2785[] - *Separate artifacts for the Jakarta and javax builds.*
+The Jakarta build publishes `tapestry-core-jakarta`, `tapestry-ioc-jakarta` 
and so on, while the javax build keeps publishing `tapestry-core`, 
`tapestry-ioc` and so on.
+This lets both live side by side in Maven Central at the same version, so 
projects that cannot move to Jakarta yet can still pick up 5.9.
+* issue:2793[] - *New 
javadoc:org.apache.tapestry5.corelib.components.Recursive[] component.*
+A javadoc:org.apache.tapestry5.corelib.components.Loop[]-like component that 
renders its body recursively over parent/child data such as trees and menus, 
with javadoc:org.apache.tapestry5.corelib.components.RecursiveBody[] marking 
the insertion point for the children.
+Objects are adapted to the component through 
javadoc:org.apache.tapestry5.commons.RecursiveValueProvider[] implementations.
+This was contributed by https://www.pubfactory.com[KGL PubFactory].
+* issue:2799[] - *Session locking is now explicit.*
+Reading from a javadoc:org.apache.tapestry5.http.services.Session[] could lock 
up a thread.
+The fix introduces `Session.LockMode` (`NONE`, `READ`, `WRITE`) together with 
`getAttributeNames(LockMode)` and `getAttributeNames(String, LockMode)` 
overloads, so the lock a call acquires is now something you can control.
+See xref:session-storage.adoc#_session_locking[Session Locking].
+* issue:2782[] - The client-side `t5/core/ajax` function accepts an 
`options.headers` object, so application code can add custom headers to Ajax 
requests.
+* issue:2789[] - A migration tool was added to help rewrite Maven and Gradle 
builds onto the suffixed artifact names.
+* A large round of dependency upgrades came with the Jakarta move, including 
Hibernate Validator 8, EclipseLink 3.0, Spring 6, Jetty 11 and Apache Commons 
FileUpload 2.
+
+[IMPORTANT]
+====
+`tapestry-spring` now depends on Spring 6, which requires *Java 17*.
+The rest of Tapestry still builds for Java 8 bytecode and is tested on Java 11 
and 17.
+====
+
+=== New Features
+* issue:2793[] - Create a component for rendering recursive data
+* issue:2789[] - Create Maven configuration tool to assist with suffixed 
artifacts
+* issue:2785[] - Change build so it generates separate artifacts for jakarta- 
and javax-based subprojects
+
+=== Improvements Made
+* issue:2800[] - Stylesheets not imported when 
javadoc:org.apache.tapestry5.annotations.Import[label=@Import] also imports a 
library when in multiple classloader mode
+* issue:2796[] - Upgrade some more dependencies: TestNG, EasyMock, 
commons-codec, ANTLR, HSQLDB, snakeyaml
+* issue:2795[] - Upgrade commons-lang dependency to 3.17.0
+* issue:2788[] - Further changes to upgrade to Jakarta EE
+* issue:2782[] - `ajax.coffee` should support more options
+* issue:2780[] - javadoc:org.apache.tapestry5.json.JSONObject[] does not 
conform to equals/hashCode contract
+* issue:2778[] - JUnit upgrade to support Eclipse 2023-09+
+* issue:2777[] - Update the closure compiler for better support of modern 
JavaScript
+
+=== Dependency Upgrades
+* issue:2798[] - Change selenium-java and selenium-leg-rc from api to 
testImplementation
+* issue:2771[] - Upgrade to Spock 2.3
+* issue:2741[] - Update to Jakarta EE 9
+
+=== Bugs Fixed
+* issue:2799[] - Thread lockup when trying to read from a Session object
+* issue:2794[] - Upgrade away from vulnerable commons-io and 
commons-fileupload dependencies
+* issue:2792[] - POST with body throws exception
+* issue:2791[] - CssCompressor for CSS files produces wrong minification for 
the `url()` function, at least with data URLs
+* issue:2790[] - Palette with option groups within the 
javadoc:org.apache.tapestry5.SelectModel[] does not work properly
+* issue:2787[] - Infinite loop in `PageSourceImpl` with multiple classloader 
mode on
+* issue:2786[] - Attempt to override final method in subclass when in multiple 
classloader mode
+* issue:2783[] - Hibernate module initialization fails when entities with 
compound ids exist
+* issue:2779[] - Tapestry 5.8.3 to 5.8.7 issues caused by the multiple 
classloader feature
+* issue:2775[] - `ClassNotFoundException` when in production mode and 
parameter type is array
+* issue:2773[] - `NullPointerException` in 5.8.5 when in production mode and 
template extension points
+* issue:2772[] - `ClassCastException` happening in some cases in production 
mode
+* issue:2770[] - With multiple classloader mode on, `ClassCastException` when 
component and superclass both have 
javadoc:org.apache.tapestry5.annotations.Import[label=@Import]
+* issue:2605[] - JS minimizer regression bug in 5.5.0-beta-1
+* issue:2563[] - Session replication fails with `NotSerializableException` on 
Tapestry class
diff --git a/modules/ROOT/pages/session-storage.adoc 
b/modules/ROOT/pages/session-storage.adoc
index be63556..22ba618 100644
--- a/modules/ROOT/pages/session-storage.adoc
+++ b/modules/ROOT/pages/session-storage.adoc
@@ -250,3 +250,48 @@ public static void 
contributeApplicationDefaults(MappedConfiguration<String,Stri
   // ...
 }
 ----
+
+=== Controlling the lock per call
+
+Locking used to be entirely implicit, which made it possible for a thread to 
lock up while reading from the session (issue:2799[]).
+Since 5.9, javadoc:org.apache.tapestry5.http.services.Session[] exposes the 
lock it acquires through `Session.LockMode`:
+
+`NONE`:: No lock is acquired.
+`READ`:: A shared read lock is acquired.
+`WRITE`:: An exclusive write lock is acquired.
+
+Each accessor has an overload taking a `LockMode`, alongside the original 
method which keeps its previous default:
+
+[%autowidth]
+|===
+|Method |Default lock
+
+|`getAttribute(String)`
+|`WRITE`
+
+|`getAttributeNames()`, `getAttributeNames(String prefix)`
+|`READ`
+
+|`containsAttribute(String)`
+|`READ`
+|===
+
+[source,java]
+----
+@Inject
+private Request request;
+
+public boolean isCartPresent()
+{
+  Session session = request.getSession(false);
+
+  // A shared read lock is enough to look at what is there.
+  return session != null
+      && session.containsAttribute(CART_ATTRIBUTE, Session.LockMode.READ);
+}
+----
+
+The actual lock taken also depends on what the current thread already holds: 
if a thread already owns a write lock, asking for a read lock does not 
downgrade it.
+
+NOTE: `containsAttribute(String)` and `containsAttribute(String, LockMode)` 
were both added in 5.9.
+Passing `LockMode.NONE` skips locking for a single call without turning 
`tapestry.session-locking-enabled` off for the whole application.
diff --git a/modules/ROOT/pages/uploading-files.adoc 
b/modules/ROOT/pages/uploading-files.adoc
index 6537c01..d473734 100644
--- a/modules/ROOT/pages/uploading-files.adoc
+++ b/modules/ROOT/pages/uploading-files.adoc
@@ -1,6 +1,17 @@
 = Uploading Files
 
-Tapestry provides a file upload component based on 
https://commons.apache.org/fileupload/[Apache Commons FileUpload] to make it 
easier to handle files uploaded through web forms (via the standard `<input 
type="file">` HTML element).
+Tapestry provides a file upload component based on 
https://commons.apache.org/proper/commons-fileupload/[Apache Commons 
FileUpload] to make it easier to handle files uploaded through web forms (via 
the standard `<input type="file">` HTML element).
+
+[IMPORTANT]
+====
+Since 5.9, `tapestry-upload` builds on Commons FileUpload 2 
(`commons-fileupload2-jakarta`) instead of the 1.x line.
+Two things are visible to applications:
+
+* `org.apache.commons.fileupload.FileUploadException` moved to 
`org.apache.commons.fileupload2.core.FileUploadException`.
+* The protected `MultipartDecoderImpl.createFileUpload()` method now returns 
`JakartaServletFileUpload` instead of `ServletFileUpload`, which matters if you 
subclass it.
+
+See xref:jakarta-ee-migration.adoc[].
+====
 
 == Downloading
 `tapestry-upload` is not automatically included in Tapestry applications 
because of the additional dependencies it requires.
diff --git a/modules/ROOT/pages/using-jsr-330-standard-annotations.adoc 
b/modules/ROOT/pages/using-jsr-330-standard-annotations.adoc
index d26dc1a..aaea026 100644
--- a/modules/ROOT/pages/using-jsr-330-standard-annotations.adoc
+++ b/modules/ROOT/pages/using-jsr-330-standard-annotations.adoc
@@ -3,9 +3,14 @@
 JSR-330 annotations can be used for injection in Tapestry 5.3 and later.
 The following table demonstrates that most of annotations in Tapestry IoC and 
JSR-330 are interchangeable. However, there are few differences in semantics.
 
+NOTE: Since 5.9 the annotations come from 
`jakarta.inject:jakarta.inject-api:2.0.1` rather than 
`javax.inject:javax.inject:1`.
+The annotation names are unchanged; only the package differs.
+If you are using the javax build of 5.9, read `javax.inject` wherever this 
page says `jakarta.inject`.
+See xref:jakarta-ee-migration.adoc[].
+
 [%autowidth]
 |===
-|JSR-330 `javax.inject` |Tapestry `org.apache.tapestry5.ioc.annotations` 
|Comment
+|JSR-330 `jakarta.inject` |Tapestry `org.apache.tapestry5.ioc.annotations` 
|Comment
 
 |@Inject
 |@Inject
@@ -66,8 +71,8 @@ For this purpose JSR-330 provides the `@Named` annotation 
which accompanies the
 
 [source,java]
 ----
-import javax.inject.Inject;
-import javax.inject.Named;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
 
 ...
 
@@ -148,14 +153,14 @@ public class Index {
    @Inject
    private Request request;
 
-   @javax.inject.Inject
+   @jakarta.inject.Inject
    private ComponentResources resources;
 
-   @javax.inject.Inject
+   @jakarta.inject.Inject
    @Named("FrenchGreeter")
    private Greeter greeter;
 
-   @javax.inject.Inject
+   @jakarta.inject.Inject
    @Symbol(SymbolConstants.PRODUCTION_MODE)
    private boolean productionMode;
 
@@ -186,7 +191,7 @@ In JSR-330 a qualifier annotation like `@French` in the 
example above needs to b
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
[email protected]
[email protected]
 public @interface French {
 }
 ----

Reply via email to