This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch feature/WW-5256-freemarker-compress in repository https://gitbox.apache.org/repos/asf/struts.git
commit ebefd53632d17b790a9a4bbecafc8fa145b6566b Author: Lukasz Lenart <[email protected]> AuthorDate: Wed Sep 24 09:32:32 2025 +0200 WW-5256 Prepares a research paper to be used to prepare an implementation plan --- ...24-WW-5256-freemarker-whitespace-compression.md | 172 +++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/thoughts/shared/research/2025-09-24-WW-5256-freemarker-whitespace-compression.md b/thoughts/shared/research/2025-09-24-WW-5256-freemarker-whitespace-compression.md new file mode 100644 index 000000000..5f85d53bc --- /dev/null +++ b/thoughts/shared/research/2025-09-24-WW-5256-freemarker-whitespace-compression.md @@ -0,0 +1,172 @@ +--- +date: 2025-09-24T09:18:27+02:00 +topic: "WW-5256 FreeMarker Whitespace Compression Implementation Analysis" +tags: [research, codebase, freemarker, compression, whitespace, WW-5256, configuration] +status: complete +--- + +# Research: WW-5256 FreeMarker Whitespace Compression Implementation Analysis + +**Date**: 2025-09-24T09:18:27+02:00 + +## Research Question +Analyze JIRA issue WW-5256 and FreeMarker whitespace documentation to determine what changes are needed to fulfill the requirements, ensuring compression is optional and can be disabled in devMode or via configuration flag. + +## Summary +WW-5256 requests reducing HTML output size from FreeMarker templates. A comprehensive implementation already exists in a feature branch but hasn't been merged. The current main branch has basic FreeMarker whitespace stripping hardcoded as enabled. To fulfill the requirements, we need to: + +1. **Merge the existing compress tag implementation** from feature branch `feature/WW-5256-compress` +2. **Add configuration option** for FreeMarker whitespace stripping (currently hardcoded) +3. **Integrate DevMode awareness** for both features (partially exists in compress tag) + +## Detailed Findings + +### Current Implementation Status + +#### Main Branch (Production) +- **FreeMarker whitespace stripping**: Hardcoded as enabled in `FreemarkerManager.java:345` +- **No configuration control**: Cannot be disabled through Struts properties +- **No compress tag**: Dedicated compression functionality doesn't exist + +#### Feature Branch (`feature/WW-5256-compress`) +- **Dedicated compress tag**: Complete implementation with `<s:compress>` tag +- **DevMode integration**: Automatically disables compression in development mode +- **HTML compression algorithm**: Uses regex `body.trim().replaceAll(">\\s+<", "><")` +- **Force override**: `force` attribute allows overriding DevMode behavior + +### FreeMarker Documentation Analysis + +#### Available Compression Techniques +1. **Whitespace Stripping**: Removes indentation and trailing whitespace from lines with only FTL tags +2. **Compress Directive**: `<#compress>` removes excess whitespace from generated output +3. **Manual Control**: `<#t>`, `<#rt>`, `<#lt>`, `<#nt>` directives for fine-grained control + +#### Best Practices +- Compression should be configurable based on environment (development vs production) +- Multiple compression levels available for different use cases +- Template-level control allows fine-tuning specific outputs + +### Required Changes for Full Implementation + +#### 1. Configuration System Integration + +**New Configuration Constants** (add to `StrutsConstants.java`): +```java +public static final String STRUTS_FREEMARKER_WHITESPACE_STRIPPING = "struts.freemarker.whitespaceStripping"; +public static final String STRUTS_FREEMARKER_COMPRESSION_ENABLED = "struts.freemarker.compression.enabled"; +``` + +**Default Properties** (add to `default.properties`): +```properties +# FreeMarker whitespace stripping (currently hardcoded as true) +struts.freemarker.whitespaceStripping = true + +# FreeMarker HTML compression tag (new feature) +struts.freemarker.compression.enabled = true +``` + +#### 2. FreemarkerManager Modifications + +**Current Code** (`FreemarkerManager.java:345`): +```java +LOG.debug("Enabled whitespace stripping"); +configuration.setWhitespaceStripping(true); // Hardcoded! +``` + +**Required Change**: +```java +@Inject(value = StrutsConstants.STRUTS_FREEMARKER_WHITESPACE_STRIPPING, required = false) +public void setWhitespaceStripping(String whitespaceStripping) { + this.whitespaceStripping = BooleanUtils.toBoolean(whitespaceStripping); +} + +// In createConfiguration() method: +if (whitespaceStripping && !devMode) { + LOG.debug("Enabled whitespace stripping"); + configuration.setWhitespaceStripping(true); +} else { + LOG.debug("Disabled whitespace stripping (devMode: {})", devMode); + configuration.setWhitespaceStripping(false); +} +``` + +#### 3. Compress Component Enhancement + +**Merge from Feature Branch**: +- `core/src/main/java/org/apache/struts2/components/Compress.java` +- `core/src/main/java/org/apache/struts2/views/jsp/CompressTag.java` +- Test files: `CompressTest.java`, `CompressTagTest.java` +- Documentation files + +**Add Global Configuration Support**: +```java +@Inject(value = StrutsConstants.STRUTS_FREEMARKER_COMPRESSION_ENABLED, required = false) +public void setCompressionEnabled(String compressionEnabled) { + this.compressionEnabled = BooleanUtils.toBoolean(compressionEnabled); +} + +@Inject(StrutsConstants.STRUTS_DEVMODE) +public void setDevMode(String devMode) { + this.devMode = BooleanUtils.toBoolean(devMode); +} +``` + +## Code References + +### Current Implementation +- `core/src/main/java/org/apache/struts2/views/freemarker/FreemarkerManager.java:345` - Hardcoded whitespace stripping +- `core/src/main/java/org/apache/struts2/StrutsConstants.java` - Configuration constants +- `core/src/main/resources/org/apache/struts2/default.properties` - Default configuration values + +### Feature Branch Implementation +- Commit: `a98ba7717d391e643b578501086d1b40f82d9ca4` - "WW-5256 Implements dedicated tag to compress output" +- `feature/WW-5256-compress` branch contains complete compress tag implementation + +### Configuration Patterns +- `core/src/main/java/org/apache/struts2/components/Component.java` - DevMode injection pattern +- `core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java` - DevMode auto-configuration + +## Architecture Insights + +### Established Configuration Patterns +1. **Dependency Injection**: Use `@Inject` with `StrutsConstants` for configuration +2. **Boolean Conversion**: Use `BooleanUtils.toBoolean()` for string-to-boolean conversion +3. **DevMode Integration**: Check `devMode` flag to automatically adjust behavior +4. **Optional Parameters**: Use `required = false` for optional configuration + +### Security Considerations +- DevMode awareness prevents unexpected behavior in development +- Simple regex-based compression algorithm avoids complex parsing vulnerabilities +- Configuration allows administrators to control feature availability + +### Performance Considerations +- Whitespace stripping happens during template compilation (minimal runtime cost) +- HTML compression happens during output generation (measurable runtime cost) +- Configuration allows disabling features when not needed + +## Implementation Strategy + +### Phase 1: Make Current Whitespace Stripping Configurable +1. Add configuration constant and default value +2. Modify `FreemarkerManager` to inject and use the configuration +3. Add DevMode awareness to disable stripping in development + +### Phase 2: Merge Compress Tag Implementation +1. Cherry-pick or merge the feature branch implementation +2. Add global configuration support to the compress component +3. Ensure proper integration with existing DevMode patterns + +### Phase 3: Testing and Documentation +1. Test both features with various configuration combinations +2. Update documentation for new configuration options +3. Verify DevMode behavior works as expected + +## Open Questions +1. Should whitespace stripping be automatically disabled in DevMode (like the compress tag) or require explicit configuration? +2. Should there be different compression levels or is the current binary on/off sufficient? +3. Should the compress tag respect the global whitespace stripping setting or remain independent? + +## Related Research +- WW-5256: Reduce size of generate html out of freemarker tag templates +- FreeMarker Documentation: Whitespace handling best practices +- Struts Configuration Patterns: DevMode and optional feature implementation \ No newline at end of file
