This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts-site.git
The following commit(s) were added to refs/heads/main by this push:
new 59e8f447e DOCS: Refine @StrutsParameter annotation documentation (#282)
59e8f447e is described below
commit 59e8f447eaf4a99b0dce012a9afa7bcdac353000
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sat Oct 18 14:29:17 2025 +0200
DOCS: Refine @StrutsParameter annotation documentation (#282)
The documentation for the `@StrutsParameter` annotation has been updated to
provide clearer and more accurate guidance on its usage, especially in
combination with checkboxes and collections.
- Clarified the placement of the annotation based on the field type
(simple, checkbox, complex object/collection) and the intended population
behavior.
- Corrected the examples to reflect the proper usage.
- Added a section to the `checkbox-interceptor.md` documentation to explain
how to use `@StrutsParameter` with checkbox lists.
---
GEMINI.md | 45 ++++++++
source/core-developers/checkbox-interceptor.md | 47 +++++++--
.../core-developers/struts-parameter-annotation.md | 117 +++++++++++++++++----
3 files changed, 181 insertions(+), 28 deletions(-)
diff --git a/GEMINI.md b/GEMINI.md
new file mode 100644
index 000000000..632a1c899
--- /dev/null
+++ b/GEMINI.md
@@ -0,0 +1,45 @@
+# Apache Struts Website
+
+## Project Overview
+
+This project contains the source code for the official Apache Struts website
(https://struts.apache.org/). It is a static website generated by Jekyll. The
site's content is written in Markdown and HTML. The project uses Maven to
manage the build process, including fetching and converting documentation from
Confluence.
+
+**Key Technologies:**
+
+* **Jekyll:** A static site generator written in Ruby.
+* **Maven:** A build automation tool used to manage the project's build
lifecycle.
+* **Java:** Used for the custom tools that fetch and convert documentation
from Confluence.
+* **HTML/CSS/JavaScript:** The core technologies used for the website's
frontend.
+* **Markdown (kramdown):** Used for writing the content of the website.
+
+**Architecture:**
+
+The website is structured as a typical Jekyll project. The main configuration
is in `_config.yml`. The source files are in the `source` directory. The
website's layout and includes are in the `_layouts` and `_includes`
directories, respectively. The documentation is partially sourced from
Confluence and converted to Markdown using custom Java tools managed by Maven.
+
+## Building and Running
+
+To build and run the website locally, you can use either Bundler and Jekyll
directly or Docker.
+
+**Using Bundler and Jekyll:**
+
+1. Install Bundler: `gem install bundler`
+2. Install dependencies: `bundle install`
+3. Run the Jekyll server: `bundle exec jekyll serve -w --trace --host 0.0.0.0`
+
+The website will be available at http://localhost:4000.
+
+**Using Docker:**
+
+There are shell scripts available to run the website in a Docker container:
+
+* `./docker-run.sh` (for Bash/Sh)
+* `./docker-run.fish` (for Fish shell)
+
+These scripts will build the Docker image and run the Jekyll server. The
website will be available at http://localhost:4000.
+
+## Development Conventions
+
+* **Content:** Content is written in Markdown (`.md`) or HTML (`.html`)
files in the `source` directory.
+* **Styling:** The website uses Bootstrap for styling. Custom styles are in
the `source/css` directory.
+* **Documentation:** The documentation is a mix of Markdown files and
content imported from the Struts Confluence wiki. The import process is managed
by Maven profiles.
+* **Updating the Website:** The `README.md` file contains instructions on
how to update the live website.
diff --git a/source/core-developers/checkbox-interceptor.md
b/source/core-developers/checkbox-interceptor.md
index a3e1857ea..38aa2fc72 100644
--- a/source/core-developers/checkbox-interceptor.md
+++ b/source/core-developers/checkbox-interceptor.md
@@ -8,19 +8,50 @@ parent:
# Checkbox Interceptor
-This interceptor is defined in the `defaultStack`. It checks each form
parameter submitted to the action and if it
-finds one with a prefix of `_checkbox` it inserts a value for a parameter
whose name is derived from the suffix
-to `_checkbox` if it does not exist. The default value inserted is `false` but
this can be changed by setting
-the `uncheckedValue` parameter on the interceptor.
+This interceptor is defined in the `defaultStack`. It is essential for
handling HTML checkboxes, as unchecked checkboxes are not submitted as part of
a form. This interceptor ensures that a value is always present for a checkbox,
so that in the Action class, the property is not `null`.
-This means that a checkbox can be accompanied by a hidden input with the same
name but a prefix of `_checkbox` so that
-if the checkbox is not checked on the form the action will still receive a
value rather than the default HTML action
-of not providing a value for unchecked checkboxes.
+## How it works
+
+The interceptor looks for a special hidden field in the form that is
associated with the checkbox. This hidden field must have a name that starts
with `__checkbox_` followed by the name of the checkbox. For example, if your
checkbox is named `myCheckbox`, the hidden field should be named
`__checkbox_myCheckbox`.
+
+When the form is submitted, the `CheckboxInterceptor` does the following:
+1. It iterates through the request parameters.
+2. If it finds a parameter that starts with `__checkbox_`, it extracts the
name of the checkbox from it (e.g., `myCheckbox`).
+3. It then checks if a parameter with the checkbox's name (`myCheckbox`)
exists in the request.
+4. If the checkbox parameter does not exist (which means the checkbox was
unchecked), the interceptor adds a new parameter to the request with the
checkbox's name and a value of `false`.
+5. Finally, it removes the `__checkbox_` prefixed parameters from the request,
so they are not processed further.
+
+This ensures that the Action property for the checkbox will be set to `false`
instead of being `null`.
+
+The `<s:checkbox>` tag from the Struts UI Tags library automatically generates
this hidden field for you.
## Parameters
- - `uncheckedValue` - the default value of an unchecked box can be overridden
by setting the `uncheckedValue` property.
+ - `uncheckedValue` - The default value for an unchecked box is `false`. You
can override this by setting the `uncheckedValue` property on the interceptor.
## Extending the Interceptor
This interceptor does not have any known extension points.
+
+## Checkbox lists usage with @StrutsParameter
+
+The `<s:checkboxlist>` tag is used to render a list of checkboxes. When using
this tag, the submitted values are populated into a `Collection` or an array in
your Action.
+When using `@StrutsParameter` with a checkbox list, you must place the
annotation on the setter method of the collection property.
+
+### Example
+
+```java
+public class MyAction extends ActionSupport {
+ private Collection<String> mySelection;
+
+ @StrutsParameter
+ public void setMySelection(Collection<String> mySelection) {
+ this.mySelection = mySelection;
+ }
+
+ @StrutsParameter
+ public Collection<String> getMySelection() {
+ return mySelection;
+ }
+}
+```
diff --git a/source/core-developers/struts-parameter-annotation.md
b/source/core-developers/struts-parameter-annotation.md
index 75bf508f3..1e043dcd6 100644
--- a/source/core-developers/struts-parameter-annotation.md
+++ b/source/core-developers/struts-parameter-annotation.md
@@ -14,38 +14,115 @@ Why it matters: by default (when annotations are
required), Struts will only inj
## Usage
-Used to annotate public _getter/setter_ methods or _fields_ on Action classes
that are intended for parameter injection
+The placement of the `@StrutsParameter` annotation is crucial and depends on
how you want to populate your action properties.
-## Parameters
+- **On a public setter method:** Place the annotation on a setter method when
you want to populate the property with a value from the request. This applies
to:
+ - Simple types (String, int, boolean, etc.).
+ - Checkboxes (single or multiple values).
+ - Collections and Maps, when you are populating the whole collection/map
from the request.
-- `depth` controls how deep into nested objects parameters can be set:
+- **On a public getter method:** Place the annotation on a getter method when
you want to allow populating the properties of the object returned by the
getter. The `depth` parameter is used to control how deep the object graph can
be populated. This is typically used for complex objects or collections of
complex objects.
+
+- **On a public field:** For simple types, you can place the annotation
directly on the public field as a shorthand for a setter annotation.
## Examples
+### Simple field
+
+Annotating the field:
```java
public class MyAction {
@StrutsParameter
public String username; // ✅ Can receive request parameter
+}
+```
+Annotating the setter:
+```java
+public class MyAction {
+ private String username;
+ @StrutsParameter
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
+```
- public String password; // ❌ Cannot receive request parameter (not
annotated)
+### Checkbox
+
+For a single checkbox, the annotation must be on the setter.
+```java
+public class MyAction {
+ private boolean myCheckbox;
+
+ @StrutsParameter
+ public void setMyCheckbox(boolean myCheckbox) {
+ this.myCheckbox = myCheckbox;
+ }
+ // ... getter
}
```
-The `depth` controls how deep into nested objects parameters can be set:
-- `depth = 0` (default): Only sets values directly on your action
- ```
+### Collections
+
+#### Populating a collection of simple types
+
+When populating a collection of simple types (e.g., from a checkbox list),
annotate the setter.
+```java
+public class MyAction {
+ private List<String> mySelection;
+
@StrutsParameter
- public String name; // Accepts: ?name=value
- ```
-- `depth = 1`: Allows one level of nesting
- ```
+ public void setMySelection(List<String> mySelection) {
+ this.mySelection = mySelection;
+ }
+ // ... getter
+}
+```
+
+#### Populating properties of objects within a collection
+
+When populating properties of objects that are already in a collection,
annotate the getter.
+```java
+public class MyAction {
+ private List<User> users; // assume this is initialized in the constructor
or elsewhere
+
+ @StrutsParameter(depth = 1)
+ public List<User> getUsers() {
+ return users;
+ }
+ // ...
+}
+```
+This allows requests like `users[0].name=John`.
+
+### Complex object
+
+#### Populating the object itself
+
+To populate the whole object from the request (e.g., using a custom type
converter), annotate the setter.
+```java
+public class MyAction {
+ private User user;
+
+ @StrutsParameter
+ public void setUser(User user) {
+ this.user = user;
+ }
+ // ... getter
+}
+```
+
+#### Populating properties of a complex object
+
+To populate the properties of a complex object, annotate the getter.
+```java
+public class MyAction {
+ private User user = new User();
+
@StrutsParameter(depth = 1)
- public User user; // Accepts: ?user.name=value
- ```
-- `depth = 2`: Allows two levels of nesting
- ```
- @StrutsParameter(depth = 2)
- public User user; // Accepts: ?user.address.city=value
- ```
-
-Rule of thumb: The depth equals the number of dots (or brackets) allowed in
the parameter name.
+ public User getUser() {
+ return user;
+ }
+}
+```
+This allows requests like `user.name=John`.