This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch gemini-docs-strutsparameter in repository https://gitbox.apache.org/repos/asf/struts-site.git
commit b0c2efa62f1b267841dc36262838ab2498090ca7 Author: Lukasz Lenart <[email protected]> AuthorDate: Sat Oct 18 14:16:42 2025 +0200 DOCS: Refine @StrutsParameter annotation documentation 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`.
