This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/struts-site.git
The following commit(s) were added to refs/heads/asf-site by this push:
new 638c3c6d2 Automatic Site Publish by Buildbot
638c3c6d2 is described below
commit 638c3c6d23bce6505ac4c75cba494554485c2229
Author: buildbot <[email protected]>
AuthorDate: Tue Aug 11 11:02:48 2026 +0000
Automatic Site Publish by Buildbot
---
.../action-file-upload-interceptor.html | 39 +++++++-
output/core-developers/action-file-upload.html | 104 +++++++++++++++++++--
output/core-developers/csp-interceptor.html | 10 ++
output/core-developers/file-upload.html | 11 ++-
output/core-developers/localization.html | 39 ++++++++
output/core-developers/logging.html | 11 +++
.../struts-parameter-annotation.html | 23 ++++-
.../type-conversion-annotation.html | 30 +++++-
output/core-developers/type-conversion.html | 6 +-
output/core-developers/validation.html | 23 +++++
output/core-developers/wildcard-mappings.html | 29 +++++-
output/plugins/cdi/index.html | 23 +++++
output/plugins/json/index.html | 49 ++++++++++
output/plugins/rest/index.html | 5 +
14 files changed, 383 insertions(+), 19 deletions(-)
diff --git a/output/core-developers/action-file-upload-interceptor.html
b/output/core-developers/action-file-upload-interceptor.html
index 3e42505a3..39b6cc164 100644
--- a/output/core-developers/action-file-upload-interceptor.html
+++ b/output/core-developers/action-file-upload-interceptor.html
@@ -184,6 +184,8 @@ You can override the text of these messages by providing
text for the following
types specified</li>
<li><code class="language-plaintext
highlighter-rouge">struts.messages.error.file.extension.not.allowed</code> -
occurs when the uploaded file does not match the expected
file extensions specified</li>
+ <li><code class="language-plaintext
highlighter-rouge">struts.messages.error.upload.policy.unresolved</code> -
occurs when a <code class="language-plaintext highlighter-rouge">${...}</code>
parameter could not be resolved for the
+current invocation, so the file is rejected (since Struts 7.3.0)</li>
</ul>
<h2 id="parameters">Parameters</h2>
@@ -216,10 +218,43 @@ enabling per-request dynamic validation. This is
available when used with <code
<p>The expressions are evaluated against the ValueStack at the time of the
upload, allowing your action to provide
dynamic values based on the current request context.</p>
+<p>Since Struts 7.3.0 the resolved values are held in a per-invocation <code
class="language-plaintext
highlighter-rouge">org.apache.struts2.interceptor.UploadPolicy</code>
+instead of being written onto the shared interceptor instance, so two
concurrent requests can no longer be validated
+against each other’s policy — see <a
href="https://issues.apache.org/jira/browse/WW-5659">WW-5659</a>.</p>
+
+<p class="alert alert-warning">Also since Struts 7.3.0, an expression that
cannot be resolved makes the policy unusable and the upload is
<strong>rejected</strong>
+with the <code class="language-plaintext
highlighter-rouge">struts.messages.error.upload.policy.unresolved</code>
message, rather than silently validated against a
+partially-resolved policy. A typo in a <code class="language-plaintext
highlighter-rouge">${...}</code> parameter, or a parameter name with no
matching property, now fails
+closed instead of relaxing validation. The <code class="language-plaintext
highlighter-rouge">disabled</code> parameter is exempt: unresolved it is simply
<code class="language-plaintext highlighter-rouge">false</code>, which leaves
+the interceptor running with the rest of the policy intact.</p>
+
<h2 id="extending-the-interceptor">Extending the Interceptor</h2>
-<p>You can extend this interceptor and override the acceptFile method to
provide more control over which files are supported
-and which are not.</p>
+<p>You can extend this interceptor and override the <code
class="language-plaintext highlighter-rouge">acceptFile</code> method to
provide more control over which files are
+supported and which are not.</p>
+
+<p>Since Struts 7.3.0 <code class="language-plaintext
highlighter-rouge">acceptFile</code> takes the per-invocation policy as its
first argument:</p>
+
+<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="kd">protected</span> <span
class="kt">boolean</span> <span class="nf">acceptFile</span><span
class="o">(</span><span class="nc">UploadPolicy</span> <span
class="n">policy</span><span class="o">,</span> <span class="nc">Object</span>
<span class="n">action</span><span class="o">,</span> <span
class="nc">UploadedFile</span> <span class="n">file</span><span
class="o">,</span>
+ <span class="nc">String</span> <span
class="n">originalFilename</span><span class="o">,</span> <span
class="nc">String</span> <span class="n">contentType</span><span
class="o">,</span> <span class="nc">String</span> <span
class="n">inputName</span><span class="o">)</span>
+</code></pre></div></div>
+
+<p class="alert alert-warning">Read <code class="language-plaintext
highlighter-rouge">policy.getMaximumSize()</code>, <code
class="language-plaintext highlighter-rouge">policy.getAllowedTypes()</code>
and <code class="language-plaintext
highlighter-rouge">policy.getAllowedExtensions()</code> instead of the former
+interceptor fields, which are configuration-time state only. Subclasses
overriding the old five-argument signature must
+be updated — the old method is gone, so the compiler will point them out.</p>
+
+<p>The same release changed the <code class="language-plaintext
highlighter-rouge">org.apache.struts2.interceptor.WithLazyParams</code>
interface, which is now generic over an
+<code class="language-plaintext
highlighter-rouge">org.apache.struts2.interceptor.InterceptorParams</code>
holder:</p>
+
+<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="kd">public</span> <span
class="kd">interface</span> <span class="nc">WithLazyParams</span><span
class="o"><</span><span class="no">P</span> <span class="kd">extends</span>
<span class="nc">InterceptorParams</span><span class="o">></span> <span
class="o">{</span>
+ <span class="no">P</span> <span class="nf">newLazyParams</span><span
class="o">();</span>
+ <span class="nc">String</span> <span class="nf">intercept</span><span
class="o">(</span><span class="nc">ActionInvocation</span> <span
class="n">invocation</span><span class="o">,</span> <span class="no">P</span>
<span class="n">lazyParams</span><span class="o">)</span> <span
class="kd">throws</span> <span class="nc">Exception</span><span
class="o">;</span>
+<span class="o">}</span>
+</code></pre></div></div>
+
+<p class="alert alert-warning">Third-party interceptors implementing <code
class="language-plaintext highlighter-rouge">WithLazyParams</code> must supply
a holder class (extend
+<code class="language-plaintext
highlighter-rouge">org.apache.struts2.interceptor.DisableParams</code> if the
interceptor supports the <code class="language-plaintext
highlighter-rouge">disabled</code> parameter) and move the
+resolved values off the singleton into it.</p>
<h2 id="examples">Examples</h2>
diff --git a/output/core-developers/action-file-upload.html
b/output/core-developers/action-file-upload.html
index cad8b9f8f..cfb36360a 100644
--- a/output/core-developers/action-file-upload.html
+++ b/output/core-developers/action-file-upload.html
@@ -164,11 +164,13 @@
<li><a href="#uploading-multiple-files"
id="markdown-toc-uploading-multiple-files">Uploading Multiple Files</a></li>
<li><a href="#advanced-configuration"
id="markdown-toc-advanced-configuration">Advanced Configuration</a> <ul>
<li><a href="#files-number-limit"
id="markdown-toc-files-number-limit">Files Number Limit</a></li>
+ <li><a href="#parameters-number-limit"
id="markdown-toc-parameters-number-limit">Parameters Number Limit</a></li>
<li><a href="#file-size-limits" id="markdown-toc-file-size-limits">File
Size Limits</a></li>
<li><a href="#normal-field-size-limit"
id="markdown-toc-normal-field-size-limit">Normal Field Size Limit</a></li>
<li><a href="#file-types" id="markdown-toc-file-types">File
Types</a></li>
<li><a href="#error-messages" id="markdown-toc-error-messages">Error
Messages</a></li>
<li><a href="#temporary-directories"
id="markdown-toc-temporary-directories">Temporary Directories</a></li>
+ <li><a href="#in-memory-uploads"
id="markdown-toc-in-memory-uploads">In-Memory Uploads</a></li>
<li><a href="#alternate-libraries"
id="markdown-toc-alternate-libraries">Alternate Libraries</a></li>
<li><a href="#request-validation"
id="markdown-toc-request-validation">Request validation</a></li>
<li><a href="#disabling-file-upload-support"
id="markdown-toc-disabling-file-upload-support">Disabling file upload
support</a></li>
@@ -315,6 +317,7 @@ it necessary to change these values. The names and default
values are:</p>
struts.multipart.saveDir= # Filesystem location to save parsed request data
struts.multipart.maxSize=2097152 # Max combined size of files per request
struts.multipart.maxFiles=256 # Max number of files per request
+struts.multipart.maxParameterCount=256 # Max number of normal fields per
request (since Struts 7.3.0)
struts.multipart.maxFileSize= # Max size per file per request
struts.multipart.maxStringLength=4096 # Max length of a string parameter (a
normal field) in a multipart request (since Struts 6.1.2.1)
</code></pre></div></div>
@@ -324,9 +327,8 @@ further details on these options first.</p>
<h3 id="files-number-limit">Files Number Limit</h3>
-<p>Since Struts 6.1.2 a new option was added, which uses Commons FileUpload
feature to limit how many files can be
-uploaded at once, in one request. This option requires to use Commons
FileUpload ver. 1.5 at least and by default is set
-to <strong>256</strong>. Please always set this to a finite value to prevent
DoS attacks.</p>
+<p>Since Struts 6.1.2 a new option was added to limit how many files can be
uploaded at once, in one request. By default it
+is set to <strong>256</strong>. Please always set this to a finite value to
prevent DoS attacks.</p>
<p>To change this value define a constant in <code class="language-plaintext
highlighter-rouge">struts.xml</code> as follows:</p>
@@ -335,8 +337,32 @@ to <strong>256</strong>. Please always set this to a
finite value to prevent DoS
<span class="nt"></struts></span>
</code></pre></div></div>
-<p class="alert alert-warning"><strong>Note</strong>: This limit also affects
number of normal fields in the request, there is an open bug in the Commons
FileUpload
-library to address this problem, see <a
href="https://issues.apache.org/jira/browse/FILEUPLOAD-351">FILEUPLOAD-351</a>.</p>
+<h3 id="parameters-number-limit">Parameters Number Limit</h3>
+
+<blockquote>
+ <p>Since Struts 7.3.0</p>
+</blockquote>
+
+<p><code class="language-plaintext
highlighter-rouge">struts.multipart.maxFiles</code> now counts <strong>uploaded
files only</strong>, and a companion option
+<code class="language-plaintext
highlighter-rouge">struts.multipart.maxParameterCount</code> (default
<strong>256</strong>) limits the number of normal, non-file fields in a
multipart
+request. The two limits are independent: a request may carry up to <code
class="language-plaintext highlighter-rouge">maxFiles</code> files
<strong>and</strong> up to <code class="language-plaintext
highlighter-rouge">maxParameterCount</code>
+form fields. Both are enforced identically by the <code
class="language-plaintext highlighter-rouge">jakarta</code> and the <code
class="language-plaintext highlighter-rouge">jakarta-stream</code> parser.</p>
+
+<div class="language-xml highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="nt"><struts></span>
+ <span class="nt"><constant</span> <span class="na">name=</span><span
class="s">"struts.multipart.maxFiles"</span> <span
class="na">value=</span><span class="s">"500"</span><span
class="nt">/></span>
+ <span class="nt"><constant</span> <span class="na">name=</span><span
class="s">"struts.multipart.maxParameterCount"</span> <span
class="na">value=</span><span class="s">"1000"</span><span
class="nt">/></span>
+<span class="nt"></struts></span>
+</code></pre></div></div>
+
+<p>Exceeding either limit is fail-closed: parsing is aborted, an upload error
is recorded, and the action receives <strong>no</strong>
+parameters and <strong>no</strong> files — never a partially populated
request.</p>
+
+<p class="alert alert-warning"><strong>Behaviour change in Struts
7.3.0</strong>: before 7.3.0, <code class="language-plaintext
highlighter-rouge">struts.multipart.maxFiles</code> capped files <em>and</em>
normal fields
+together (<code class="language-plaintext highlighter-rouge">jakarta</code>
parser) or counted distinct file field names (<code class="language-plaintext
highlighter-rouge">jakarta-stream</code> parser), so a form with many normal
+fields and few files could be rejected — see <a
href="https://issues.apache.org/jira/browse/WW-5474">WW-5474</a>. Such requests
now
+pass, while a request with more than <code class="language-plaintext
highlighter-rouge">maxParameterCount</code> normal fields is rejected with the
new
+<code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.FileUploadParameterCountLimitException</code>
message. If you raised <code class="language-plaintext
highlighter-rouge">maxFiles</code> only to
+accommodate large forms, lower it back to a realistic file count and raise
<code class="language-plaintext
highlighter-rouge">struts.multipart.maxParameterCount</code> instead.</p>
<h3 id="file-size-limits">File Size Limits</h3>
@@ -451,17 +477,25 @@ messages by providing text for the following keys:</p>
<td>Occurs when uploaded file has disallowed extension</td>
</tr>
<tr>
- <td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.SizeLimitExceededException</code></td>
- <td>Occurs when the upload request (as a whole) exceed configured
<strong>struts.multipart.maxSize</strong></td>
+ <td><code class="language-plaintext
highlighter-rouge">struts.messages.error.upload.policy.unresolved</code></td>
+ <td>Occurs when a <code class="language-plaintext
highlighter-rouge">${...}</code> interceptor parameter could not be resolved,
see <a
href="action-file-upload-interceptor#dynamic-parameter-evaluation">Dynamic
Parameter Evaluation</a> (since Struts 7.3.0)</td>
</tr>
<tr>
- <td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.FileSizeLimitExceededException</code></td>
+ <td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.FileUploadSizeException</code></td>
+ <td>Occurs when the upload request (as a whole) exceeds configured
<strong>struts.multipart.maxSize</strong></td>
+ </tr>
+ <tr>
+ <td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.FileUploadByteCountLimitException</code></td>
<td>Occurs when a file within the upload request exceeds configured
<strong>struts.multipart.maxFileSize</strong></td>
</tr>
<tr>
- <td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.FileCountLimitExceededException</code></td>
+ <td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.FileUploadFileCountLimitException</code></td>
<td>Occurs when the number of files in the upload request exceeds
configured <strong>struts.multipart.maxFiles</strong></td>
</tr>
+ <tr>
+ <td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.FileUploadParameterCountLimitException</code></td>
+ <td>Occurs when the number of normal fields in the upload request
exceeds configured <strong>struts.multipart.maxParameterCount</strong> (since
Struts 7.3.0)</td>
+ </tr>
<tr>
<td><code class="language-plaintext
highlighter-rouge">struts.messages.upload.error.<Exception class
SimpleName></code></td>
<td>Occurs when any other exception took place during file upload
process</td>
@@ -469,6 +503,10 @@ messages by providing text for the following keys:</p>
</tbody>
</table>
+<p class="alert alert-info">The exception-based keys above are the Commons
FileUpload 2 names used since Struts 7.0.0. Struts 6.x uses the Commons
+FileUpload 1.x names instead: <code class="language-plaintext
highlighter-rouge">SizeLimitExceededException</code>, <code
class="language-plaintext
highlighter-rouge">FileSizeLimitExceededException</code>
+and <code class="language-plaintext
highlighter-rouge">FileCountLimitExceededException</code>.</p>
+
<h3 id="temporary-directories">Temporary Directories</h3>
<p>All uploaded files are saved to a temporary directory by the framework
before being passed in to an Action. Depending on
@@ -478,6 +516,54 @@ to the directory where the uploaded files will be placed.
If this property is no
to <code class="language-plaintext
highlighter-rouge">javax.servlet.context.tempdir</code>. Keep in mind that on
some operating systems, like Solaris, <code class="language-plaintext
highlighter-rouge">/tmp</code> is memory based
and files stored in that directory would consume an amount of RAM
approximately equal to the size of the uploaded file.</p>
+<h3 id="in-memory-uploads">In-Memory Uploads</h3>
+
+<blockquote>
+ <p>Since Struts 7.3.0</p>
+</blockquote>
+
+<p>Small uploads (below the parser’s disk-spill threshold, around 8 KB) are
kept in memory and are <strong>no longer written to a
+temporary file eagerly</strong> — see <a
href="https://issues.apache.org/jira/browse/WW-5413">WW-5413</a>. The temporary
file is written
+lazily, only when something asks for a <code class="language-plaintext
highlighter-rouge">java.io.File</code>. Uploads rejected by size, content-type
or extension checks
+therefore never touch the filesystem at all.</p>
+
+<p>Two methods were added to <code class="language-plaintext
highlighter-rouge">org.apache.struts2.dispatcher.multipart.UploadedFile</code>
to support this. Both are <code class="language-plaintext
highlighter-rouge">default</code>
+methods, so existing third-party implementations keep compiling:</p>
+
+<table>
+ <thead>
+ <tr>
+ <th>Method</th>
+ <th>Purpose</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code class="language-plaintext highlighter-rouge">InputStream
getInputStream()</code></td>
+ <td>Reads the uploaded content without forcing it to disk — the
preferred way to consume an upload</td>
+ </tr>
+ <tr>
+ <td><code class="language-plaintext highlighter-rouge">boolean
isMissing()</code></td>
+ <td>Reports a failed upload with no content, answered without
materialising the content</td>
+ </tr>
+ </tbody>
+</table>
+
+<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="kd">public</span> <span
class="kt">void</span> <span class="nf">withUploadedFiles</span><span
class="o">(</span><span class="nc">List</span><span class="o"><</span><span
class="nc">UploadedFile</span><span class="o">></span> <span
class="n">uploadedFiles</span><span class="o">)</span> <span class="o">{</span>
+ <span class="k">for</span> <span class="o">(</span><span
class="nc">UploadedFile</span> <span class="n">file</span> <span
class="o">:</span> <span class="n">uploadedFiles</span><span class="o">)</span>
<span class="o">{</span>
+ <span class="k">try</span> <span class="o">(</span><span
class="nc">InputStream</span> <span class="n">in</span> <span
class="o">=</span> <span class="n">file</span><span class="o">.</span><span
class="na">getInputStream</span><span class="o">())</span> <span
class="o">{</span>
+ <span class="c1">// process the bytes; no temporary file is
created for small uploads</span>
+ <span class="o">}</span> <span class="k">catch</span> <span
class="o">(</span><span class="nc">IOException</span> <span
class="n">e</span><span class="o">)</span> <span class="o">{</span>
+ <span class="c1">// handle</span>
+ <span class="o">}</span>
+ <span class="o">}</span>
+<span class="o">}</span>
+</code></pre></div></div>
+
+<p class="alert alert-info"><code class="language-plaintext
highlighter-rouge">getContent()</code> and <code class="language-plaintext
highlighter-rouge">getAbsolutePath()</code> still return a <code
class="language-plaintext highlighter-rouge">java.io.File</code> as before, so
existing code — including actions
+using the legacy <code class="language-plaintext
highlighter-rouge">File</code>-typed property — keeps working unchanged; the
first such call simply materialises the temporary
+file at that point. Prefer <code class="language-plaintext
highlighter-rouge">getInputStream()</code> in new code when you only need the
bytes.</p>
+
<h3 id="alternate-libraries">Alternate Libraries</h3>
<p>The <code class="language-plaintext
highlighter-rouge">struts.multipart.parser</code> used by the fileUpload
interceptor to handle HTTP POST requests, encoded using the
diff --git a/output/core-developers/csp-interceptor.html
b/output/core-developers/csp-interceptor.html
index 7a309e5fb..671e36602 100644
--- a/output/core-developers/csp-interceptor.html
+++ b/output/core-developers/csp-interceptor.html
@@ -200,6 +200,16 @@ Read JavaDoc of the action for more details.</p>
<p>Note: the action must always return an HTTP status <code
class="language-plaintext highlighter-rouge">204</code>.</p>
</blockquote>
+<p>Since Struts 6.11.0 and 7.3.0 the submitted report body is read up to a
bounded length instead of being read whole.
+The limit defaults to <strong>8192</strong> characters and is configurable:</p>
+
+<div class="language-xml highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="nt"><constant</span> <span
class="na">name=</span><span class="s">"struts.csp.report.maxSize"</span> <span
class="na">value=</span><span class="s">"16384"</span><span
class="nt">/></span>
+</code></pre></div></div>
+
+<p>A report larger than the limit is discarded with a warning and <code
class="language-plaintext highlighter-rouge">processReport(String)</code> is
not called. Accepted values
+are <code class="language-plaintext highlighter-rouge">1</code> to <code
class="language-plaintext highlighter-rouge">1048576</code>; anything outside
that range is ignored, and the default applies. Raise the limit if your
+browsers submit larger violation reports than the default allows.</p>
+
<h2 id="action-aware">Action aware</h2>
<p>Since Struts 6.2.0 it is possible to configure the CSP interceptor by
providing the an instance of <code class="language-plaintext
highlighter-rouge">CspSettings</code> interface.
diff --git a/output/core-developers/file-upload.html
b/output/core-developers/file-upload.html
index dda0d70e8..c9fd7c966 100644
--- a/output/core-developers/file-upload.html
+++ b/output/core-developers/file-upload.html
@@ -438,6 +438,7 @@ it necessary to change these values. The names and default
values are:</p>
struts.multipart.saveDir= # Filesystem location to save parsed request data
struts.multipart.maxSize=2097152 # Max combined size of files per request
struts.multipart.maxFiles=256 # Max number of files per request
+struts.multipart.maxParameterCount=256 # Max number of normal fields per
request (since Struts 7.3.0)
struts.multipart.maxFileSize= # Max size per file per request
struts.multipart.maxStringLength=4096 # Max length of a string parameter (a
normal field) in a multipart request (since Struts 6.1.2.1)
</code></pre></div></div>
@@ -458,8 +459,10 @@ to <strong>256</strong>. Please always set this to a
finite value to prevent DoS
<span class="nt"></struts></span>
</code></pre></div></div>
-<p class="alert alert-warning"><strong>Note</strong>: This limit also affects
number of normal fields in the request, there is an open bug in the Commons
FileUpload
-library to address this problem, see <a
href="https://issues.apache.org/jira/browse/FILEUPLOAD-351">FILEUPLOAD-351</a>.</p>
+<p class="alert alert-warning"><strong>Note</strong>: Up to Struts 7.2.x this
limit also affects the number of normal fields in the request, see
+<a
href="https://issues.apache.org/jira/browse/FILEUPLOAD-351">FILEUPLOAD-351</a>.
Since Struts 7.3.0 <code class="language-plaintext
highlighter-rouge">struts.multipart.maxFiles</code>
+counts uploaded files only, and normal fields are limited separately by <code
class="language-plaintext
highlighter-rouge">struts.multipart.maxParameterCount</code>
+(default <strong>256</strong>) — see <a
href="action-file-upload#parameters-number-limit">Parameters Number
Limit</a>.</p>
<h3 id="file-size-limits">File Size Limits</h3>
@@ -591,6 +594,10 @@ messages by providing text for the following keys:</p>
</tbody>
</table>
+<p class="alert alert-info">The exception-based keys above are the Commons
FileUpload 1.x names used by Struts 6.x. Since Struts 7.0.0 the
+Commons FileUpload 2 names apply instead — see
+<a href="action-file-upload#error-messages">Error Messages</a> on the Action
File Upload page.</p>
+
<h3 id="temporary-directories">Temporary Directories</h3>
<p>All uploaded files are saved to a temporary directory by the framework
before being passed in to an Action. Depending on
diff --git a/output/core-developers/localization.html
b/output/core-developers/localization.html
index 53ccfd347..2fc3a9caf 100644
--- a/output/core-developers/localization.html
+++ b/output/core-developers/localization.html
@@ -170,6 +170,8 @@
<li><a href="#formatting-dates-and-numbers"
id="markdown-toc-formatting-dates-and-numbers">Formatting Dates and
Numbers</a></li>
<li><a href="#comparison-with-struts-1"
id="markdown-toc-comparison-with-struts-1">Comparison with Struts 1</a></li>
<li><a href="#search-in-default-bundles-first"
id="markdown-toc-search-in-default-bundles-first">Search in default bundles
first</a></li>
+ <li><a href="#tuning-the-localized-text-caches"
id="markdown-toc-tuning-the-localized-text-caches">Tuning the localized-text
caches</a></li>
+ <li><a href="#validating-the-request-locale"
id="markdown-toc-validating-the-request-locale">Validating the request
locale</a></li>
<li><a href="#using-only-global-bundles"
id="markdown-toc-using-only-global-bundles">Using only global bundles</a></li>
<li><a href="#custom-textprovider-and-textproviderfactory"
id="markdown-toc-custom-textprovider-and-textproviderfactory">Custom
TextProvider and TextProviderFactory</a></li>
</ul>
@@ -320,6 +322,43 @@ will search the default bundles first. In some cases this
can improve overall ap
<p>More details can be found in <a
href="https://issues.apache.org/jira/browse/WW-5112">WW-5112</a> and the linked
PR.</p>
</blockquote>
+<h2 id="tuning-the-localized-text-caches">Tuning the localized-text caches</h2>
+
+<blockquote>
+ <p>Since Struts 6.11.0 and 7.3.0</p>
+</blockquote>
+
+<p><code class="language-plaintext
highlighter-rouge">AbstractLocalizedTextProvider</code> caches resolved
bundles, missing-bundle misses, message formats and the class/package
+hierarchy lookups. Those caches are size-bounded and use the same cache
abstraction as the
+<a href="ognl-cache-configuration">OGNL caches</a>, configured with two
constants:</p>
+
+<div class="language-xml highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="nt"><constant</span> <span
class="na">name=</span><span class="s">"struts.i18n.cacheType"</span> <span
class="na">value=</span><span class="s">"wtlfu"</span><span
class="nt">/></span>
+<span class="nt"><constant</span> <span class="na">name=</span><span
class="s">"struts.i18n.cacheMaxSize"</span> <span class="na">value=</span><span
class="s">"10000"</span><span class="nt">/></span>
+</code></pre></div></div>
+
+<p><code class="language-plaintext
highlighter-rouge">struts.i18n.cacheType</code> accepts <code
class="language-plaintext highlighter-rouge">basic</code> (unbounded <code
class="language-plaintext highlighter-rouge">ConcurrentHashMap</code>), <code
class="language-plaintext highlighter-rouge">lru</code> or <code
class="language-plaintext highlighter-rouge">wtlfu</code> (Window TinyLFU, the
default).
+<code class="language-plaintext
highlighter-rouge">struts.i18n.cacheMaxSize</code> (default <code
class="language-plaintext highlighter-rouge">10000</code>) applies to each
cache individually. Every entry is fully reconstructible
+on a miss, so eviction only costs a recompute — it can never yield a stale or
wrong message. A very large application
+that resolves more than 10 000 distinct message keys may want to raise <code
class="language-plaintext highlighter-rouge">struts.i18n.cacheMaxSize</code> to
avoid eviction
+churn.</p>
+
+<h2 id="validating-the-request-locale">Validating the request locale</h2>
+
+<blockquote>
+ <p>Since Struts 6.11.0 and 7.3.0</p>
+</blockquote>
+
+<p>When <code class="language-plaintext
highlighter-rouge">struts.locale</code> is not set, the framework derives the
locale from the request (the <code class="language-plaintext
highlighter-rouge">Accept-Language</code> header).
+<code class="language-plaintext highlighter-rouge">Dispatcher</code> used to
take that value as-is, while <a href="i18n-interceptor">I18n Interceptor</a>
already checked its own resolved
+locale against the JVM’s available locales. Set the following constant to
apply the same check on the <code class="language-plaintext
highlighter-rouge">Dispatcher</code>
+path:</p>
+
+<div class="language-xml highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="nt"><constant</span> <span
class="na">name=</span><span
class="s">"struts.locale.validateRequestLocale"</span> <span
class="na">value=</span><span class="s">"true"</span><span
class="nt">/></span>
+</code></pre></div></div>
+
+<p>The default is <code class="language-plaintext
highlighter-rouge">false</code>, preserving the previous behaviour. With <code
class="language-plaintext highlighter-rouge">true</code>, a request-derived
locale that is not in the
+JVM’s available-locale set falls back to the default locale instead of being
used.</p>
+
<h2 id="using-only-global-bundles">Using only global bundles</h2>
<p>If you don’t need to use the package-scan-functionality and only base on
the global bundles (those provided by
diff --git a/output/core-developers/logging.html
b/output/core-developers/logging.html
index 29b63369d..2e324b4df 100644
--- a/output/core-developers/logging.html
+++ b/output/core-developers/logging.html
@@ -169,6 +169,17 @@ setting <code class="language-plaintext
highlighter-rouge">-Dxwork.loggerFactory
<p>The Log4j2 bridges for third-party libraries (<code
class="language-plaintext highlighter-rouge">log4j-jcl</code> for Commons
Logging, <code class="language-plaintext
highlighter-rouge">log4j-slf4j-impl</code> for SLF4J) are a
separate concern and remain available. They route logging emitted by
transitive dependencies into Log4j2.</p>
+<p>Struts 7.3.0 finished the job: the last <code class="language-plaintext
highlighter-rouge">java.util.logging</code> and SLF4J call sites inside the
framework — in the
+dependency-injection container, its finalizable reference queue and the Tiles
plugin’s Velocity renderable — now log
+through Log4j2, and the <code class="language-plaintext
highlighter-rouge">slf4j-api</code> dependency was dropped from <code
class="language-plaintext highlighter-rouge">struts2-core</code>, see
+<a href="https://issues.apache.org/jira/browse/WW-5620">WW-5620</a>. That
dependency was declared <code class="language-plaintext
highlighter-rouge">optional</code>, so it was never
+propagated to applications and nothing changes on your classpath. Struts
6.11.0 still declares it.</p>
+
+<p class="alert alert-warning"><strong>Removed in Struts 7.3.0</strong>: the
internal DI container no longer provides a <code class="language-plaintext
highlighter-rouge">Logger</code> binding. <code class="language-plaintext
highlighter-rouge">@Inject</code>-ing a
+<code class="language-plaintext
highlighter-rouge">java.util.logging.Logger</code> into a container-managed
bean used to yield a JUL logger named after the declaring class;
+that binding was removed along with the JUL usage, so such an injection no
longer resolves. Declare a Log4j2 logger
+directly instead, as shown below.</p>
+
<h2 id="usage">Usage</h2>
<p>Declare a Log4j2 logger in your own classes as usual:</p>
diff --git a/output/core-developers/struts-parameter-annotation.html
b/output/core-developers/struts-parameter-annotation.html
index 98cf19ea9..a55c7b7c6 100644
--- a/output/core-developers/struts-parameter-annotation.html
+++ b/output/core-developers/struts-parameter-annotation.html
@@ -155,7 +155,10 @@
<h1 class="no_toc" id="strutsparameter-annotation">StrutsParameter
Annotation</h1>
<ul id="markdown-toc">
- <li><a href="#where-authorization-applies"
id="markdown-toc-where-authorization-applies">Where authorization
applies</a></li>
+ <li><a href="#where-authorization-applies"
id="markdown-toc-where-authorization-applies">Where authorization applies</a>
<ul>
+ <li><a href="#creator-bound-properties"
id="markdown-toc-creator-bound-properties">Creator-bound properties</a></li>
+ </ul>
+ </li>
<li><a href="#modeldriven-actions"
id="markdown-toc-modeldriven-actions">ModelDriven actions</a></li>
<li><a href="#usage" id="markdown-toc-usage">Usage</a></li>
<li><a href="#understanding-the-depth-parameter"
id="markdown-toc-understanding-the-depth-parameter">Understanding the <code
class="language-plaintext highlighter-rouge">depth</code> parameter</a></li>
@@ -187,6 +190,24 @@ action chaining (opt-in via <code
class="language-plaintext highlighter-rouge">s
authorization performed during deserialization, so unauthorized fields are
never set.</li>
</ul>
+<h3 id="creator-bound-properties">Creator-bound properties</h3>
+
+<p>Up to Struts 7.2.1 the REST plugin’s authorization wrapper covered only
properties Jackson populates through a setter,
+a field or a builder. Properties bound through a <strong>constructor</strong>
— Java records, <code class="language-plaintext
highlighter-rouge">@JsonCreator</code> constructors and
+<code class="language-plaintext
highlighter-rouge">@ConstructorProperties</code> — were deserialized on a path
that bypassed the wrapper entirely, so with
+<code class="language-plaintext
highlighter-rouge">struts.parameters.requireAnnotations</code> enabled a
record-typed value anywhere in a REST request body was populated with
+no authorization check at all. This is fixed in Struts 7.3.0, see
+<a href="https://issues.apache.org/jira/browse/WW-5642">WW-5642</a>.</p>
+
+<p>Creator-bound properties are now authorized like any other property, by
their path. Values that are not authorized are
+redacted — the component is left unset rather than taking the client-supplied
value — and if a record’s own constructor
+rejects that (a compact constructor calling <code class="language-plaintext
highlighter-rouge">Objects.requireNonNull</code>, or <code
class="language-plaintext
highlighter-rouge">FAIL_ON_NULL_FOR_PRIMITIVES</code>), the whole
+object under construction is dropped instead of failing the request.</p>
+
+<p class="alert alert-warning">If a REST action relied on record-typed
request-body properties binding without annotations, they now need authorizing
+the same way as any nested object: <code class="language-plaintext
highlighter-rouge">@StrutsParameter(depth = ...)</code> on the getter that
reaches them, or a <code class="language-plaintext
highlighter-rouge">ModelDriven</code>
+model. Otherwise those values silently stop arriving.</p>
+
<h2 id="modeldriven-actions">ModelDriven actions</h2>
<p>When an action implements <code class="language-plaintext
highlighter-rouge">ModelDriven</code> and the <a
href="model-driven-interceptor.html">Model Driven
diff --git a/output/core-developers/type-conversion-annotation.html
b/output/core-developers/type-conversion-annotation.html
index 682a6e8e3..0f3a24ee3 100644
--- a/output/core-developers/type-conversion-annotation.html
+++ b/output/core-developers/type-conversion-annotation.html
@@ -174,7 +174,29 @@ file within the classpath root. Set type to: <code
class="language-plaintext hig
<h2 id="usage">Usage</h2>
-<p>The <code class="language-plaintext
highlighter-rouge">TypeConversion</code> annotation can be applied at property
and method level.</p>
+<p>The <code class="language-plaintext
highlighter-rouge">TypeConversion</code> annotation can be applied at method
level, and since Struts 7.3.0 also at field level.</p>
+
+<h2 id="key-derivation">Key derivation</h2>
+
+<blockquote>
+ <p>Since Struts 7.3.0</p>
+</blockquote>
+
+<p>The <code class="language-plaintext highlighter-rouge">key</code> parameter
accepts a bare property name for every <code class="language-plaintext
highlighter-rouge">ConversionRule</code>; the rule’s prefix is derived
+automatically. It used to be derived only for method-level annotations, so
class-level <code class="language-plaintext
highlighter-rouge">@Conversion(conversions = ...)</code>
+entries had to spell the prefix out:</p>
+
+<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="c1">// before Struts 7.3.0 — prefix
spelled out</span>
+<span class="nd">@Conversion</span><span class="o">(</span><span
class="n">conversions</span> <span class="o">=</span> <span
class="nd">@TypeConversion</span><span class="o">(</span><span
class="n">key</span> <span class="o">=</span> <span
class="s">"CreateIfNull_users"</span><span class="o">,</span> <span
class="n">rule</span> <span class="o">=</span> <span
class="nc">ConversionRule</span><span class="o">.</span><span
class="na">CREATE_IF_NULL</span><span class="o">,</span> <span class= [...]
+
+<span class="c1">// since Struts 7.3.0 — the CreateIfNull_ prefix is
derived</span>
+<span class="nd">@Conversion</span><span class="o">(</span><span
class="n">conversions</span> <span class="o">=</span> <span
class="nd">@TypeConversion</span><span class="o">(</span><span
class="n">key</span> <span class="o">=</span> <span
class="s">"users"</span><span class="o">,</span> <span class="n">rule</span>
<span class="o">=</span> <span class="nc">ConversionRule</span><span
class="o">.</span><span class="na">CREATE_IF_NULL</span><span
class="o">,</span> <span class="n">value</sp [...]
+</code></pre></div></div>
+
+<p>Existing annotations that already carry a prefix keep working: a <code
class="language-plaintext highlighter-rouge">key</code> starting with any known
rule prefix is used
+as-is, never prefixed twice. The prefixes are <code class="language-plaintext
highlighter-rouge">CreateIfNull_</code>, <code class="language-plaintext
highlighter-rouge">Element_</code>, <code class="language-plaintext
highlighter-rouge">Key_</code> and <code class="language-plaintext
highlighter-rouge">KeyProperty_</code>;
+<code class="language-plaintext
highlighter-rouge">ConversionRule.PROPERTY</code> and <code
class="language-plaintext highlighter-rouge">ConversionRule.MAP</code> have no
prefix of their own. Keys of
+<code class="language-plaintext
highlighter-rouge">ConversionType.APPLICATION</code> annotations are class
names and are never prefixed.</p>
<h2 id="parameters">Parameters</h2>
@@ -229,6 +251,10 @@ file within the classpath root. Set type to: <code
class="language-plaintext hig
</p>
+<p class="alert alert-warning"><code class="language-plaintext
highlighter-rouge">ConversionRule.COLLECTION</code> is deprecated since Struts
7.3.0 — use <code class="language-plaintext
highlighter-rouge">ConversionRule.ELEMENT</code> instead. The two are handled
+identically by the engine, and <code class="language-plaintext
highlighter-rouge">ELEMENT</code> additionally covers the values of a <code
class="language-plaintext highlighter-rouge">Map</code>. Existing annotations
using
+<code class="language-plaintext highlighter-rouge">COLLECTION</code> keep
working and produce the deprecated <code class="language-plaintext
highlighter-rouge">Collection_xxx</code> key, which is still read as a
fallback.</p>
+
<h2 id="examples">Examples</h2>
<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code> <span class="nd">@Conversion</span><span
class="o">()</span>
@@ -253,7 +279,7 @@ file within the classpath root. Set type to: <code
class="language-plaintext hig
<span class="k">this</span><span class="o">.</span><span
class="na">convertDouble</span> <span class="o">=</span> <span
class="n">convertDouble</span><span class="o">;</span>
<span class="o">}</span>
- <span class="nd">@TypeConversion</span><span class="o">(</span><span
class="n">rule</span> <span class="o">=</span> <span
class="nc">ConversionRule</span><span class="o">.</span><span
class="na">COLLECTION</span><span class="o">,</span> <span
class="n">converterClass</span> <span class="o">=</span> <span
class="nc">String</span><span class="o">.</span><span
class="na">class</span><span class="o">)</span>
+ <span class="nd">@TypeConversion</span><span class="o">(</span><span
class="n">rule</span> <span class="o">=</span> <span
class="nc">ConversionRule</span><span class="o">.</span><span
class="na">ELEMENT</span><span class="o">,</span> <span
class="n">converterClass</span> <span class="o">=</span> <span
class="nc">String</span><span class="o">.</span><span
class="na">class</span><span class="o">)</span>
<span class="nd">@StrutsParameter</span>
<span class="kd">public</span> <span class="kt">void</span> <span
class="nf">setUsers</span><span class="o">(</span> <span class="nc">List</span>
<span class="n">users</span> <span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span
class="na">users</span> <span class="o">=</span> <span
class="n">users</span><span class="o">;</span>
diff --git a/output/core-developers/type-conversion.html
b/output/core-developers/type-conversion.html
index b75f20752..8d80f2d25 100644
--- a/output/core-developers/type-conversion.html
+++ b/output/core-developers/type-conversion.html
@@ -403,8 +403,10 @@ contained within Maps and Collections. For Collections,
such as Lists, the eleme
<code class="language-plaintext highlighter-rouge">Element_xxx</code>, where
<code class="language-plaintext highlighter-rouge">xxx</code> is the field name
of the collection property in your action or object. For Maps, both the key
and the value may be specified by using the pattern <code
class="language-plaintext highlighter-rouge">Key_xxx</code> and <code
class="language-plaintext highlighter-rouge">Element_xxx</code>,
respectively.</p>
-<p>From WebWork 2.1.x, the <code class="language-plaintext
highlighter-rouge">Collection_xxx</code> format is still supported and honored,
although it is deprecated and will be
-removed eventually.</p>
+<p class="alert alert-warning">From WebWork 2.1.x, the <code
class="language-plaintext highlighter-rouge">Collection_xxx</code> format is
still supported and honored, although it is deprecated and will be
+removed eventually. Since Struts 7.3.0 the matching <code
class="language-plaintext highlighter-rouge">ConversionRule.COLLECTION</code>
enum constant used by the
+<a href="type-conversion-annotation">TypeConversion Annotation</a> is marked
<code class="language-plaintext highlighter-rouge">@Deprecated</code> as well —
use <code class="language-plaintext
highlighter-rouge">ConversionRule.ELEMENT</code>,
+which produces the current <code class="language-plaintext
highlighter-rouge">Element_xxx</code> key and additionally covers the values of
a <code class="language-plaintext highlighter-rouge">Map</code>.</p>
<p>Additionally, you can create your own custom <code
class="language-plaintext highlighter-rouge">ObjectTypeDeterminer</code> by
implementing the <code class="language-plaintext
highlighter-rouge">ObjectTypeDeterminer</code> interface.
There is also an optional <code class="language-plaintext
highlighter-rouge">ObjectTypeDeterminer</code> that utilizes Java 5 generics.
See the <a href="annotations">Annotations</a>
diff --git a/output/core-developers/validation.html
b/output/core-developers/validation.html
index 88286de1c..312080f84 100644
--- a/output/core-developers/validation.html
+++ b/output/core-developers/validation.html
@@ -174,6 +174,7 @@
<li><a href="#validator-flavor" id="markdown-toc-validator-flavor">Validator
Flavor</a></li>
<li><a href="#non-field-validator-vs-field-validator-validatortypes"
id="markdown-toc-non-field-validator-vs-field-validator-validatortypes">Non-Field
Validator Vs Field-Validator validatortypes</a></li>
<li><a href="#short-circuiting-validator"
id="markdown-toc-short-circuiting-validator">Short-Circuiting Validator</a></li>
+ <li><a href="#skipping-validators-on-a-conversion-error"
id="markdown-toc-skipping-validators-on-a-conversion-error">Skipping Validators
on a Conversion Error</a></li>
<li><a href="#how-validators-of-an-action-are-found"
id="markdown-toc-how-validators-of-an-action-are-found">How Validators of an
Action are Found</a></li>
<li><a href="#writing-custom-validators"
id="markdown-toc-writing-custom-validators">Writing custom validators</a></li>
<li><a href="#resources" id="markdown-toc-resources">Resources</a></li>
@@ -737,6 +738,28 @@ syntax was used to declare the validation rule. By way of
example, given this <c
and will not short-circuit the plain ExpressionValidator because
FieldValidators only short-circuit other checks on that
same field. Since the plain Validator is not field specific, it is not
short-circuited.</p>
+<h2 id="skipping-validators-on-a-conversion-error">Skipping Validators on a
Conversion Error</h2>
+
+<blockquote>
+ <p>Since Struts 7.3.0</p>
+</blockquote>
+
+<p>When a field fails <a href="type-conversion">type conversion</a> — a user
typing <code class="language-plaintext highlighter-rouge">one</code> into an
<code class="language-plaintext highlighter-rouge">Integer age</code> field,
for example —
+the <a href="conversion-error-interceptor">Conversion Error Interceptor</a>
records a conversion error before the validators run.
+The field’s own validators then run against a value the user never entered, so
a <code class="language-plaintext highlighter-rouge">requiredstring</code> or
<code class="language-plaintext highlighter-rouge">int</code> validator
+typically adds a second, redundant error for the same field.</p>
+
+<p>Set the following constant to skip a field’s remaining validators once that
field has a conversion error:</p>
+
+<div class="language-xml highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="nt"><struts></span>
+ <span class="nt"><constant</span> <span class="na">name=</span><span
class="s">"struts.validators.skipValidatorsOnConversionError"</span> <span
class="na">value=</span><span class="s">"true"</span><span
class="nt">/></span>
+<span class="nt"></struts></span>
+</code></pre></div></div>
+
+<p>The default is <code class="language-plaintext
highlighter-rouge">false</code>, which preserves the long-standing behaviour of
reporting both errors. The <code class="language-plaintext
highlighter-rouge">conversion</code> field
+validator itself is never skipped, so a custom conversion message and the
<code class="language-plaintext highlighter-rouge">repopulateField</code>
behaviour of
+<a href="conversion-validator">Conversion Validator</a> keep working.
Action-level (non-field) validators are unaffected.</p>
+
<h2 id="how-validators-of-an-action-are-found">How Validators of an Action are
Found</h2>
<p>As mentioned above, the framework will also search up the inheritance tree
of the action to find default validations
diff --git a/output/core-developers/wildcard-mappings.html
b/output/core-developers/wildcard-mappings.html
index eab34584c..7f48b9caa 100644
--- a/output/core-developers/wildcard-mappings.html
+++ b/output/core-developers/wildcard-mappings.html
@@ -155,7 +155,10 @@
<h1 class="no_toc" id="wildcard-mappings">Wildcard Mappings</h1>
<ul id="markdown-toc">
- <li><a href="#wildcards" id="markdown-toc-wildcards">Wildcards</a></li>
+ <li><a href="#wildcards" id="markdown-toc-wildcards">Wildcards</a> <ul>
+ <li><a href="#ordering-of-annotated-wildcard-actions"
id="markdown-toc-ordering-of-annotated-wildcard-actions">Ordering of annotated
wildcard actions</a></li>
+ </ul>
+ </li>
<li><a href="#parameters-in-namespaces"
id="markdown-toc-parameters-in-namespaces">Parameters in namespaces</a></li>
<li><a href="#parameters-after-the-action-name"
id="markdown-toc-parameters-after-the-action-name">Parameters after the action
name</a></li>
<li><a href="#advanced-wildcards"
id="markdown-toc-advanced-wildcards">Advanced Wildcards</a></li>
@@ -244,6 +247,30 @@ the <code class="language-plaintext
highlighter-rouge">{0}</code> token.</p>
<p>See also <a href="../getting-started/wildcard-method-selection">Wildcard
Method</a></p>
</blockquote>
+<h3 id="ordering-of-annotated-wildcard-actions">Ordering of annotated wildcard
actions</h3>
+
+<blockquote>
+ <p>Since Struts 7.3.0</p>
+</blockquote>
+
+<p>The ordering rule above relies on the <em>physical order</em> of the
mappings, which XML gives you but annotations do not:
+actions declared with the <a href="../plugins/convention/">Convention
Plugin</a>’s <code class="language-plaintext highlighter-rouge">@Action</code>
were registered in class-scan order,
+which is effectively arbitrary and can differ between JVMs and classloaders. A
broad pattern registered ahead of a
+narrower one it also matches could therefore shadow it, non-deterministically
— see
+<a href="https://issues.apache.org/jira/browse/WW-3784">WW-3784</a>.</p>
+
+<p>Annotated wildcard action names are now sorted most-specific-first before
registration, using these keys in order:</p>
+
+<ol>
+ <li>fewer wildcard tokens first (a <code class="language-plaintext
highlighter-rouge">*</code> / <code class="language-plaintext
highlighter-rouge">**</code> run, or a <code class="language-plaintext
highlighter-rouge">{var}</code> group);</li>
+ <li>more literal characters first;</li>
+ <li>fewer path-spanning <code class="language-plaintext
highlighter-rouge">**</code> tokens first;</li>
+ <li>alphabetical order of the pattern, as a deterministic tie-breaker.</li>
+</ol>
+
+<p>The comparison recognises both <code class="language-plaintext
highlighter-rouge">*</code> / <code class="language-plaintext
highlighter-rouge">**</code> (the default <code class="language-plaintext
highlighter-rouge">WildcardHelper</code> matcher) and <code
class="language-plaintext highlighter-rouge">{var}</code>
+(<code class="language-plaintext
highlighter-rouge">NamedVariablePatternMatcher</code>). XML mappings are
unaffected — they keep their declared order.</p>
+
<h2 id="parameters-in-namespaces">Parameters in namespaces</h2>
<p>From Struts 2.1+ namespace patterns can be extracted as request parameters
and bound to the action. To enable this
diff --git a/output/plugins/cdi/index.html b/output/plugins/cdi/index.html
index 0b2e9159e..310e930b5 100644
--- a/output/plugins/cdi/index.html
+++ b/output/plugins/cdi/index.html
@@ -160,6 +160,7 @@
<li><a href="#maven-setup" id="markdown-toc-maven-setup">Maven
Setup</a></li>
<li><a href="#manual-integration"
id="markdown-toc-manual-integration">Manual Integration</a></li>
<li><a href="#configuration"
id="markdown-toc-configuration">Configuration</a></li>
+ <li><a href="#proxied-beans" id="markdown-toc-proxied-beans">Proxied
beans</a></li>
</ul>
</li>
<li><a href="#usage" id="markdown-toc-usage">Usage</a> <ul>
@@ -302,6 +303,28 @@ the right name. Just add a constant like this to your
struts.xml to be on the ha
</code></pre></div></div>
+<h3 id="proxied-beans">Proxied beans</h3>
+
+<blockquote>
+ <p>Since Struts 7.3.0</p>
+</blockquote>
+
+<p>Normal-scoped CDI beans (<code class="language-plaintext
highlighter-rouge">@SessionScoped</code>, <code class="language-plaintext
highlighter-rouge">@RequestScoped</code>, <code class="language-plaintext
highlighter-rouge">@ApplicationScoped</code>, …) are injected as client proxies.
+Struts resolves the real target class of a proxy before evaluating the OGNL
member allowlist, but only knew about
+Spring and Hibernate proxies — a Weld client proxy was not recognised, so
members reached through it were evaluated
+against the proxy class, see <a
href="https://issues.apache.org/jira/browse/WW-5604">WW-5604</a>.</p>
+
+<p>The plugin now registers its own <code class="language-plaintext
highlighter-rouge">ProxyService</code> implementation, which adds Weld
client-proxy detection on top of the
+default one:</p>
+
+<div class="language-xml highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="nt"><bean</span> <span
class="na">type=</span><span
class="s">"org.apache.struts2.util.ProxyService"</span> <span
class="na">name=</span><span class="s">"cdi"</span> <span
class="na">class=</span><span
class="s">"org.apache.struts2.cdi.CdiProxyService"</span><span
class="nt">/></span>
+<span class="nt"><constant</span> <span class="na">name=</span><span
class="s">"struts.proxyService"</span> <span class="na">value=</span><span
class="s">"cdi"</span><span class="nt">/></span>
+</code></pre></div></div>
+
+<p>Both lines ship in the plugin’s <code class="language-plaintext
highlighter-rouge">struts-plugin.xml</code>, so nothing has to be configured.
Weld detection activates only
+when Weld is on the classpath; with any other CDI implementation the plugin
behaves exactly as the default
+<code class="language-plaintext highlighter-rouge">ProxyService</code> did.
Override <code class="language-plaintext
highlighter-rouge">struts.proxyService</code> only if you supply your own
implementation.</p>
+
<h2 id="usage">Usage</h2>
<p>CDI has an extremely rich feature set, and this section is not intended as
a replacement for the CDI reference
diff --git a/output/plugins/json/index.html b/output/plugins/json/index.html
index efe51fe41..148bd3f26 100644
--- a/output/plugins/json/index.html
+++ b/output/plugins/json/index.html
@@ -166,6 +166,7 @@
<li><a href="#prefix" id="markdown-toc-prefix">Prefix</a></li>
<li><a href="#base-classes" id="markdown-toc-base-classes">Base
Classes</a></li>
<li><a href="#enumerations"
id="markdown-toc-enumerations">Enumerations</a></li>
+ <li><a href="#java-records-and-optional"
id="markdown-toc-java-records-and-optional">Java records and Optional</a></li>
<li><a href="#compressing-the-output"
id="markdown-toc-compressing-the-output">Compressing the output</a></li>
<li><a href="#preventing-the-browser-from-caching-the-response"
id="markdown-toc-preventing-the-browser-from-caching-the-response">Preventing
the browser from caching the response</a></li>
<li><a href="#excluding-properties-with-null-values"
id="markdown-toc-excluding-properties-with-null-values">Excluding properties
with null values</a></li>
@@ -488,6 +489,39 @@ All properties of the enum are also serialized.</p>
<span class="nt"></result></span>
</code></pre></div></div>
+<h3 id="java-records-and-optional">Java records and Optional</h3>
+
+<blockquote>
+ <p>Since Struts 7.3.0</p>
+</blockquote>
+
+<p>Java records are serialized by iterating their record components, so a
record no longer has to be wrapped in a bean to
+be returned as JSON:</p>
+
+<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="kd">public</span> <span
class="n">record</span> <span class="nf">Person</span><span
class="o">(</span><span class="nc">String</span> <span
class="n">name</span><span class="o">,</span> <span class="kt">int</span> <span
class="n">age</span><span class="o">)</span> <span class="o">{}</span>
+</code></pre></div></div>
+
+<div class="language-json highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="p">{</span><span
class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"Alice"</span><span class="p">,</span><span class="w"> </span><span
class="nl">"age"</span><span class="p">:</span><span class="w"> </span><span
class="mi">30</span><span class="p">}</span><span class="w">
+</span></code></pre></div></div>
+
+<p>The <code class="language-plaintext highlighter-rouge">@JSON</code> and
<code class="language-plaintext highlighter-rouge">@JSONFieldBridge</code>
annotations now also target record components, so they can be declared on the
+component itself and are honoured on the generated accessor:</p>
+
+<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="kd">public</span> <span
class="n">record</span> <span class="nf">Person</span><span
class="o">(</span><span class="nc">String</span> <span
class="n">name</span><span class="o">,</span> <span
class="nd">@JSON</span><span class="o">(</span><span class="n">name</span>
<span class="o">=</span> <span class="s">"years"</span><span class="o">)</span>
<span class="kt">int</span> <span cl [...]
+</code></pre></div></div>
+
+<p>The <code class="language-plaintext
highlighter-rouge">excludeProperties</code> / <code class="language-plaintext
highlighter-rouge">includeProperties</code> result parameters apply to record
components as they do to bean
+properties.</p>
+
+<p><code class="language-plaintext
highlighter-rouge">java.util.Optional</code> values are serialized as the
contained value, or as <code class="language-plaintext
highlighter-rouge">null</code> when empty — an empty <code
class="language-plaintext highlighter-rouge">Optional</code> is
+therefore subject to <code class="language-plaintext
highlighter-rouge">excludeNullProperties</code> like any other null:</p>
+
+<div class="language-java highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="kd">public</span> <span
class="n">record</span> <span class="nf">Person</span><span
class="o">(</span><span class="nc">String</span> <span
class="n">name</span><span class="o">,</span> <span
class="nc">Optional</span><span class="o"><</span><span
class="nc">String</span><span class="o">></span> <span
class="n">nickname</span><span class="o">)</span> <span class="o">{}</span>
+</code></pre></div></div>
+
+<div class="language-json highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="p">{</span><span
class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"Alice"</span><span class="p">,</span><span class="w"> </span><span
class="nl">"nickname"</span><span class="p">:</span><span class="w">
</span><span class="kc">null</span><span class="p">}</span><span class="w">
+</span></code></pre></div></div>
+
<h3 id="compressing-the-output">Compressing the output</h3>
<p>Set the <code class="language-plaintext
highlighter-rouge">enableGZIP</code> attribute to true to gzip the generated
json response. The request <strong>must</strong> include <code
class="language-plaintext highlighter-rouge">gzip</code>
@@ -585,6 +619,16 @@ The implementation should then be defined in <code
class="language-plaintext hig
<span class="nt"></struts></span>
</code></pre></div></div>
+<p class="alert alert-warning">The <code class="language-plaintext
highlighter-rouge">struts.json.writer</code> and <code
class="language-plaintext highlighter-rouge">struts.json.reader</code>
overrides were ignored in Struts 7.2.x — the plugin’s bean selection
+ran before the application’s <code class="language-plaintext
highlighter-rouge">struts.xml</code> was folded in, freezing the binding to the
built-in implementations. This is
+fixed in Struts 7.3.0, see <a
href="https://issues.apache.org/jira/browse/WW-5641">WW-5641</a>; if you
carried a workaround for
+that regression, you can drop it.</p>
+
+<p class="alert alert-warning">Declare the bean with <code
class="language-plaintext highlighter-rouge">scope="prototype"</code>, as in
the example above. Since Struts 7.3.0 the <code class="language-plaintext
highlighter-rouge">json</code> interceptor obtains a
+fresh reader and writer per request, and the built-in implementations keep
their parse/write state in plain instance
+fields — cross-request safety relies on the bean being prototype-scoped. A
singleton-scoped custom implementation that
+holds per-request state will leak it between concurrent requests.</p>
+
<p>There is an example at <a
href="https://gitbox.apache.org/repos/asf?p=struts-examples.git;a=blob_plain;f=json-customize/src/main/java/org/demo/FlexJSONWriter.java;hb=HEAD">struts-examples/json-customize/FlexJSONWriter.java</a>.
It replaces Struts default json serializer with <a
href="http://flexjson.sourceforge.net/">Flexjson</a> as below:</p>
@@ -807,6 +851,11 @@ a <code class="language-plaintext
highlighter-rouge"><param></code> (follo
<div class="language-xml highlighter-rouge"><div class="highlight"><pre
class="highlight"><code><span class="nt"><constant</span> <span
class="na">name=</span><span class="s">"struts.json.maxLength"</span> <span
class="na">value=</span><span class="s">"1048576"</span><span
class="nt">/></span>
</code></pre></div></div>
+<p class="alert alert-info">Since Struts 6.11.0 and 7.3.0 <code
class="language-plaintext highlighter-rouge">struts.json.maxLength</code> is
evaluated <strong>while</strong> the input is read, in fixed-size chunks,
+rather than after accumulating each line, so enforcement no longer depends on
how the payload is split into lines. As
+part of the same change, line terminators are no longer stripped while
reading: they are insignificant whitespace
+between tokens, but an unescaped control character inside a string value is
now preserved rather than silently removed.</p>
+
<p>The reader and writer implementations are also pluggable via
<code class="language-plaintext highlighter-rouge">struts.json.reader</code>
and <code class="language-plaintext
highlighter-rouge">struts.json.writer</code> (both default to <code
class="language-plaintext highlighter-rouge">struts</code>).</p>
diff --git a/output/plugins/rest/index.html b/output/plugins/rest/index.html
index cc0336b56..a9e0c7f43 100644
--- a/output/plugins/rest/index.html
+++ b/output/plugins/rest/index.html
@@ -551,6 +551,11 @@ same property-level authorization should implement
<code class="language-plaintext
highlighter-rouge">AuthorizationAwareContentTypeHandler</code> (which extends
<code class="language-plaintext highlighter-rouge">ContentTypeHandler</code>
with
authorization callbacks) instead of <code class="language-plaintext
highlighter-rouge">ContentTypeHandler</code> directly.</p>
+<p class="alert alert-warning">Since Struts 7.3.0 that enforcement also covers
<strong>constructor-bound</strong> properties — Java records, <code
class="language-plaintext highlighter-rouge">@JsonCreator</code>
+constructors and <code class="language-plaintext
highlighter-rouge">@ConstructorProperties</code> — which previously bypassed
it. If your controllers accept records in the
+request body, make sure they are reachable from an authorized property; see
+<a
href="../../core-developers/struts-parameter-annotation.html#creator-bound-properties">Creator-bound
properties</a>.</p>
+
<h3 id="settings">Settings</h3>
<p>The following settings can be customized. See the <a
href="/core-developers/configuration-files">developer guide</a>.