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

lukaszlenart pushed a commit to branch docs/struts-7.3.0-content-audit
in repository https://gitbox.apache.org/repos/asf/struts-site.git

commit b8ff92560957d646cd28d8bea246c3bd49a926ac
Author: Lukasz Lenart <[email protected]>
AuthorDate: Mon Aug 3 07:29:43 2026 +0200

    docs: document Struts 7.3.0 multipart and upload-policy changes
    
    WW-5474: struts.multipart.maxFiles now counts uploaded files only, and the
    new struts.multipart.maxParameterCount (default 256) limits normal fields.
    Both limits are fail-closed and enforced identically by the jakarta and
    jakarta-stream parsers.
    
    WW-5413: small uploads stay in memory; the temporary file is written lazily.
    Document the new UploadedFile.getInputStream() / isMissing() default 
methods.
    
    WW-5659: lazy ${...} interceptor params resolve into a per-invocation
    UploadPolicy; an unresolved expression now rejects the upload. Document the
    acceptFile(UploadPolicy, ...) signature change and the WithLazyParams break.
    
    Also correct the upload error-message keys: Struts 7.x uses the Commons
    FileUpload 2 exception names, Struts 6.x the 1.x ones.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 .../action-file-upload-interceptor.md              | 44 +++++++++-
 source/core-developers/action-file-upload.md       | 94 ++++++++++++++++++----
 source/core-developers/file-upload.md              | 12 ++-
 3 files changed, 131 insertions(+), 19 deletions(-)

diff --git a/source/core-developers/action-file-upload-interceptor.md 
b/source/core-developers/action-file-upload-interceptor.md
index 52e489e8b..bc4bc9257 100644
--- a/source/core-developers/action-file-upload-interceptor.md
+++ b/source/core-developers/action-file-upload-interceptor.md
@@ -33,6 +33,8 @@ You can override the text of these messages by providing text 
for the following
    types specified
  - `struts.messages.error.file.extension.not.allowed` - occurs when the 
uploaded file does not match the expected 
    file extensions specified
+ - `struts.messages.error.upload.policy.unresolved` - occurs when a `${...}` 
parameter could not be resolved for the
+   current invocation, so the file is rejected (since Struts 7.3.0)
 
 ## Parameters
 
@@ -61,10 +63,48 @@ enabling per-request dynamic validation. This is available 
when used with `WithL
 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.
 
+Since Struts 7.3.0 the resolved values are held in a per-invocation 
`org.apache.struts2.interceptor.UploadPolicy`
+instead of being written onto the shared interceptor instance, so two 
concurrent requests can no longer be validated
+against each other's policy — see 
[WW-5659](https://issues.apache.org/jira/browse/WW-5659).
+
+Also since Struts 7.3.0, an expression that cannot be resolved makes the 
policy unusable and the upload is **rejected**
+with the `struts.messages.error.upload.policy.unresolved` message, rather than 
silently validated against a
+partially-resolved policy. A typo in a `${...}` parameter, or a parameter name 
with no matching property, now fails
+closed instead of relaxing validation. The `disabled` parameter is exempt: 
unresolved it is simply `false`, which leaves
+the interceptor running with the rest of the policy intact.
+{:.alert .alert-warning}
+
 ## Extending the Interceptor
 
-You can extend this interceptor and override the acceptFile method to provide 
more control over which files are supported 
-and which are not.
+You can extend this interceptor and override the `acceptFile` method to 
provide more control over which files are
+supported and which are not.
+
+Since Struts 7.3.0 `acceptFile` takes the per-invocation policy as its first 
argument:
+
+```java
+protected boolean acceptFile(UploadPolicy policy, Object action, UploadedFile 
file,
+                             String originalFilename, String contentType, 
String inputName)
+```
+
+Read `policy.getMaximumSize()`, `policy.getAllowedTypes()` and 
`policy.getAllowedExtensions()` 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.
+{:.alert .alert-warning}
+
+The same release changed the `org.apache.struts2.interceptor.WithLazyParams` 
interface, which is now generic over an
+`org.apache.struts2.interceptor.InterceptorParams` holder:
+
+```java
+public interface WithLazyParams<P extends InterceptorParams> {
+    P newLazyParams();
+    String intercept(ActionInvocation invocation, P lazyParams) throws 
Exception;
+}
+```
+
+Third-party interceptors implementing `WithLazyParams` must supply a holder 
class (extend
+`org.apache.struts2.interceptor.DisableParams` if the interceptor supports the 
`disabled` parameter) and move the
+resolved values off the singleton into it.
+{:.alert .alert-warning}
 
 ## Examples
 
diff --git a/source/core-developers/action-file-upload.md 
b/source/core-developers/action-file-upload.md
index 289b5090e..f19f958aa 100644
--- a/source/core-developers/action-file-upload.md
+++ b/source/core-developers/action-file-upload.md
@@ -157,6 +157,7 @@ struts.multipart.parser=jakarta
 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)
 ```
@@ -166,9 +167,8 @@ further details on these options first.
 
 ### Files Number Limit
 
-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 **256**. Please always set this to a finite value to prevent DoS attacks.
+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 **256**. Please always set this to a finite value to prevent DoS 
attacks.
 
 To change this value define a constant in `struts.xml` as follows:
 
@@ -178,8 +178,31 @@ To change this value define a constant in `struts.xml` as 
follows:
 </struts>
 ```
 
-**Note**: 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 
[FILEUPLOAD-351](https://issues.apache.org/jira/browse/FILEUPLOAD-351).
+### Parameters Number Limit
+
+> Since Struts 7.3.0
+
+`struts.multipart.maxFiles` now counts **uploaded files only**, and a 
companion option
+`struts.multipart.maxParameterCount` (default **256**) limits the number of 
normal, non-file fields in a multipart
+request. The two limits are independent: a request may carry up to `maxFiles` 
files **and** up to `maxParameterCount`
+form fields. Both are enforced identically by the `jakarta` and the 
`jakarta-stream` parser.
+
+```xml
+<struts>
+    <constant name="struts.multipart.maxFiles" value="500"/>
+    <constant name="struts.multipart.maxParameterCount" value="1000"/>
+</struts>
+```
+
+Exceeding either limit is fail-closed: parsing is aborted, an upload error is 
recorded, and the action receives **no**
+parameters and **no** files — never a partially populated request.
+
+**Behaviour change in Struts 7.3.0**: before 7.3.0, 
`struts.multipart.maxFiles` capped files *and* normal fields
+together (`jakarta` parser) or counted distinct file field names 
(`jakarta-stream` parser), so a form with many normal
+fields and few files could be rejected — see 
[WW-5474](https://issues.apache.org/jira/browse/WW-5474). Such requests now
+pass, while a request with more than `maxParameterCount` normal fields is 
rejected with the new
+`struts.messages.upload.error.FileUploadParameterCountLimitException` message. 
If you raised `maxFiles` only to
+accommodate large forms, lower it back to a realistic file count and raise 
`struts.multipart.maxParameterCount` instead.
 {:.alert .alert-warning}
 
 ### File Size Limits
@@ -274,16 +297,23 @@ or extends `com.opensymphony.xwork2.ActionSupport`. These 
error messages are bas
 struts-messages.properties, a default i18n file processed for all i18n 
requests. You can override the text of these
 messages by providing text for the following keys:
 
-| Error Key                                                      | Description 
                                                                                
           |
-|----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
-| `struts.messages.error.uploading`                              | A general 
error that occurs when the file could not be uploaded                           
             |
-| `struts.messages.error.file.too.large`                         | Occurs when 
the uploaded file is too large as specified by maximumSize.                     
           |
-| `struts.messages.error.content.type.not.allowed`               | Occurs when 
the uploaded file does not match the expected content types specified           
           |
-| `struts.messages.error.file.extension.not.allowed`             | Occurs when 
uploaded file has disallowed extension                                          
           |
-| `struts.messages.upload.error.SizeLimitExceededException`      | Occurs when 
the upload request (as a whole) exceed configured **struts.multipart.maxSize**  
           |
-| `struts.messages.upload.error.FileSizeLimitExceededException`  | Occurs when 
a file within the upload request exceeds configured 
**struts.multipart.maxFileSize**       |
-| `struts.messages.upload.error.FileCountLimitExceededException` | Occurs when 
the number of files in the upload request exceeds configured 
**struts.multipart.maxFiles** |
-| `struts.messages.upload.error.<Exception class SimpleName>`    | Occurs when 
any other exception took place during file upload process                       
           |
+| Error Key                                                             | 
Description                                                                     
                                                    |
+|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
+| `struts.messages.error.uploading`                                     | A 
general error that occurs when the file could not be uploaded                   
                                                  |
+| `struts.messages.error.file.too.large`                                | 
Occurs when the uploaded file is too large as specified by maximumSize.         
                                                    |
+| `struts.messages.error.content.type.not.allowed`                      | 
Occurs when the uploaded file does not match the expected content types 
specified                                                   |
+| `struts.messages.error.file.extension.not.allowed`                    | 
Occurs when uploaded file has disallowed extension                              
                                                    |
+| `struts.messages.error.upload.policy.unresolved`                      | 
Occurs when a `${...}` interceptor parameter could not be resolved, see 
[Dynamic Parameter 
Evaluation](action-file-upload-interceptor#dynamic-parameter-evaluation) (since 
Struts 7.3.0) |
+| `struts.messages.upload.error.FileUploadSizeException`                | 
Occurs when the upload request (as a whole) exceeds configured 
**struts.multipart.maxSize**                                         |
+| `struts.messages.upload.error.FileUploadByteCountLimitException`      | 
Occurs when a file within the upload request exceeds configured 
**struts.multipart.maxFileSize**                                    |
+| `struts.messages.upload.error.FileUploadFileCountLimitException`      | 
Occurs when the number of files in the upload request exceeds configured 
**struts.multipart.maxFiles**                              |
+| `struts.messages.upload.error.FileUploadParameterCountLimitException` | 
Occurs when the number of normal fields in the upload request exceeds 
configured **struts.multipart.maxParameterCount** (since Struts 7.3.0) |
+| `struts.messages.upload.error.<Exception class SimpleName>`           | 
Occurs when any other exception took place during file upload process           
                                                    |
+
+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: `SizeLimitExceededException`, 
`FileSizeLimitExceededException`
+and `FileCountLimitExceededException`.
+{:.alert .alert-info}
 
 ### Temporary Directories
 
@@ -294,6 +324,40 @@ to the directory where the uploaded files will be placed. 
If this property is no
 to `javax.servlet.context.tempdir`. Keep in mind that on some operating 
systems, like Solaris, `/tmp` is memory based
 and files stored in that directory would consume an amount of RAM 
approximately equal to the size of the uploaded file.
 
+### In-Memory Uploads
+
+> Since Struts 7.3.0
+
+Small uploads (below the parser's disk-spill threshold, around 8 KB) are kept 
in memory and are **no longer written to a
+temporary file eagerly** — see 
[WW-5413](https://issues.apache.org/jira/browse/WW-5413). The temporary file is 
written
+lazily, only when something asks for a `java.io.File`. Uploads rejected by 
size, content-type or extension checks
+therefore never touch the filesystem at all.
+
+Two methods were added to 
`org.apache.struts2.dispatcher.multipart.UploadedFile` to support this. Both 
are `default`
+methods, so existing third-party implementations keep compiling:
+
+| Method                            | Purpose                                  
                                                          |
+|-----------------------------------|----------------------------------------------------------------------------------------------------|
+| `InputStream getInputStream()`    | Reads the uploaded content without 
forcing it to disk — the preferred way to consume an upload      |
+| `boolean isMissing()`             | Reports a failed upload with no content, 
answered without materialising the content                 |
+
+```java
+public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
+    for (UploadedFile file : uploadedFiles) {
+        try (InputStream in = file.getInputStream()) {
+            // process the bytes; no temporary file is created for small 
uploads
+        } catch (IOException e) {
+            // handle
+        }
+    }
+}
+```
+
+`getContent()` and `getAbsolutePath()` still return a `java.io.File` as 
before, so existing code — including actions
+using the legacy `File`-typed property — keeps working unchanged; the first 
such call simply materialises the temporary
+file at that point. Prefer `getInputStream()` in new code when you only need 
the bytes.
+{:.alert .alert-info}
+
 ### Alternate Libraries
 
 The `struts.multipart.parser` used by the fileUpload interceptor to handle 
HTTP POST requests, encoded using the
diff --git a/source/core-developers/file-upload.md 
b/source/core-developers/file-upload.md
index d7811940a..46500d5c2 100644
--- a/source/core-developers/file-upload.md
+++ b/source/core-developers/file-upload.md
@@ -261,6 +261,7 @@ struts.multipart.parser=jakarta
 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)
 ```
@@ -282,8 +283,10 @@ To change this value define a constant in `struts.xml` as 
follows:
 </struts>
 ```
 
-**Note**: 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 
[FILEUPLOAD-351](https://issues.apache.org/jira/browse/FILEUPLOAD-351).
+**Note**: Up to Struts 7.2.x this limit also affects the number of normal 
fields in the request, see
+[FILEUPLOAD-351](https://issues.apache.org/jira/browse/FILEUPLOAD-351). Since 
Struts 7.3.0 `struts.multipart.maxFiles`
+counts uploaded files only, and normal fields are limited separately by 
`struts.multipart.maxParameterCount`
+(default **256**) — see [Parameters Number 
Limit](action-file-upload#parameters-number-limit).
 {:.alert .alert-warning}
 
 ### File Size Limits
@@ -388,6 +391,11 @@ messages by providing text for the following keys:
 | `struts.messages.upload.error.FileCountLimitExceededException` | Occurs when 
the number of files in the upload request exceeds configured 
**struts.multipart.maxFiles** |
 | `struts.messages.upload.error.<Exception class SimpleName>`    | Occurs when 
any other exception took place during file upload process                       
           |
 
+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
+[Error Messages](action-file-upload#error-messages) on the Action File Upload 
page.
+{:.alert .alert-info}
+
 ### Temporary Directories
 
 All uploaded files are saved to a temporary directory by the framework before 
being passed in to an Action. Depending on

Reply via email to