Skip to content

WW-5630 - Performance Issue SecurityMemberAccess#1721

Merged
lukaszlenart merged 8 commits into
apache:mainfrom
brianandle:WW-5630
Jun 12, 2026
Merged

WW-5630 - Performance Issue SecurityMemberAccess#1721
lukaszlenart merged 8 commits into
apache:mainfrom
brianandle:WW-5630

Conversation

@brianandle

@brianandle brianandle commented May 29, 2026

Copy link
Copy Markdown
Contributor

Closes WW-5630

@lukaszlenart lukaszlenart left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling WW-5630 — the motivation is solid. SecurityMemberAccess is Scope.PROTOTYPE (StrutsBeanSelectionProvider.java:454), so it's reconstructed on every value-stack creation (~per request), and each construction re-runs the @Inject use* setters → validateClassesClassLoader.loadClass(...). Memoizing that is worthwhile, and Caffeine is already a core dependency.

Two blocking issues before merge, both rooted in the cache key:

  1. Key on classloader identity, not String.valueOf(classLoader). This can return a Class<?> from the wrong loader (collisions / constant toString()), and those Class objects feed OGNL exclusion/allowlist checks that rely on == — security-adjacent.
  2. Static cache holding strong Class<?> values pins classloaders — relevant given the WW-5537 leak work on adjacent branches.

Both are solved together with Caffeine.newBuilder().maximumSize(...).weakKeys().weakValues().build() keyed on the real ClassLoader (e.g. Cache<ClassLoader, Cache<String, Class<?>>>). weakKeys() gives identity equality (fixes #1) and GC-safety (fixes #2). Details inline.

Comment thread core/src/main/java/org/apache/struts2/util/ConfigParseUtil.java Outdated
Comment thread core/src/main/java/org/apache/struts2/util/ConfigParseUtil.java Outdated
Comment thread core/src/main/java/org/apache/struts2/util/ConfigParseUtil.java
Comment thread core/src/test/java/org/apache/struts2/util/ConfigParseUtilTest.java
* Cache ClassLoader directly
* Use weakKeys and weakValues
* Comment on the ClassLookupException
* Additional Unit Tests

Assistance in coding using co-pilot
@brianandle

Copy link
Copy Markdown
Contributor Author

@lukaszlenart I believe I've resolved all the concerns you raised and added additional UTs as well.


Test set: org.apache.struts2.util.ConfigParseUtilTest

Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 s -- in org.apache.struts2.util.ConfigParseUtilTest

@lukaszlenart lukaszlenart left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick turnaround — this addresses all four points cleanly:

  • ✅ Keyed on the actual ClassLoader via a nested Cache<ClassLoader, Cache<String, Class<?>>> with weakKeys() → identity equality (no more toString() collisions) and GC-safe.
  • weakValues() so cached Class objects no longer pin their loader.
  • ClassLookupKey/classLoaderName removed; cast invariant now documented on ClassLookupException.
  • ✅ Added the same-toString() collision test plus several others.

I ran mvn test -DskipAssembly -pl core -Dtest=ConfigParseUtilTest locally — 9/9 green. Approving.

One non-blocking nuance left on the outer cache (inline) — worth a look but not a blocker.

Comment thread core/src/main/java/org/apache/struts2/util/ConfigParseUtil.java Outdated
* Limit outer, Classloader, to 25. Ensure memory bounding.
* Limit inner, Classes, to 50. Ensure memory bounding.
* Additional UTs

With co-pilot assitance
@lukaszlenart

Copy link
Copy Markdown
Member

@brianandle thanks, all looks good 💪

@lukaszlenart lukaszlenart enabled auto-merge (squash) June 12, 2026 17:09
@lukaszlenart lukaszlenart merged commit 210dc86 into apache:main Jun 12, 2026
6 checks passed
lukaszlenart pushed a commit that referenced this pull request Jun 14, 2026
* WW-5630 - Performance Issue SecurityMemberAccess
* Add size bound cache, 50, for Class lookup
* Add unit test

Code generated by Copilot

* WW-5630 - Add additional UT

* WW-5630 - Add UT for non-existent class

* WW-5630 - Review feedback changes
* Cache ClassLoader directly
* Use weakKeys and weakValues
* Comment on the ClassLookupException
* Additional Unit Tests

Assistance in coding using co-pilot

* WW-5630 - Additional review
* Limit outer, Classloader, to 25. Ensure memory bounding.
* Limit inner, Classes, to 50. Ensure memory bounding.
* Additional UTs

With co-pilot assitance

(cherry picked from commit 210dc86)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants