* WW-3784 docs: design for specificity-ordered wildcard matching in annotated actions
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 docs: implementation plan for annotated wildcard specificity ordering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 feat(convention): add action-name specificity comparator
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 fix(convention): add Apache License header to test file
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 feat(core): add PackageConfig.Builder.reorderActionConfigs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 docs: add javadoc for PackageConfig.Builder.reorderActionConfigs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 feat(convention): order annotated wildcard actions most-specific-first
Sorts each convention-built package's action configs by pattern specificity so a
specific pattern (some/usefull/*) is matched before a general one (some/*),
regardless of class-scan order. Also makes convention action ordering deterministic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 docs: correct wildcard cross-segment claims and note comparator limitations
The spec incorrectly stated that WildcardHelper's single `*` is greedy
and crosses `/`, and that `some/*` shadows `some/usefull/*`. Verified
against WildcardHelper.java and NamedVariablePatternMatcher.java: only
`**` crosses `/`, so those two patterns are actually disjoint (different
segment counts) and never compete for the same request. Correct the
Problem narrative, ticket example, and matcher bullets to state this
accurately, and document two known limitations of the specificity
comparator (raw wildcard-token-count key can misrank `**` ahead of
narrower multi-token patterns; parent-package actions bypass sorting).
Also add a test asserting the natural-order alphabetical tiebreak key.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* WW-3784 test(convention): prove specificity ordering fixes wildcard shadowing end-to-end
Adds an end-to-end routing test driving the production reorder
(PackageConfig.Builder.reorderActionConfigs + ActionNameSpecificityComparator)
through the real ActionConfigMatcher/WildcardHelper. some/** and some/usefull/*
genuinely overlap for some/usefull/sleeping (** crosses '/'), so the test asserts
the general pattern shadows the specific one when registered first, and that
specificity ordering makes the specific action reachable again.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* WW-5537 Add InternalDestroyable and ContextAwareDestroyable interfaces
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 ContainerHolder: ThreadLocal with AtomicLong generation counter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 FinalizableReferenceQueue: volatile instance, join, classloader null
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 ScopeInterceptor.clearLocks: add synchronized block
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 CompoundRootAccessor, DefaultFileManager: implement InternalDestroyable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Add InternalDestroyable adapter classes for static cache cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Register InternalDestroyable beans in struts-beans.xml
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 JSON plugin: add JSONCacheDestroyable for BeanInfo cache cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Dispatcher.cleanup: refactor into focused methods with InternalDestroyable discovery
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Rewrite DispatcherCleanupTest for InternalDestroyable discovery
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Add log4j-web for proper Log4j2 lifecycle in Servlet container
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Dispatcher.destroyObjectFactory: add early return on null, use pattern matching
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Fix @since annotations: 7.1.0 -> 7.2.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Add Container.destroy() to clear internal caches on undeploy
Container now exposes a destroy() method that clears factories, injectors,
constructors, and ThreadLocals. This releases Class<?> keys and JDK
DelegatingClassLoader instances that pin the webapp classloader.
DefaultConfiguration.destroy() calls container.destroy() and
reloadContainer() delegates to destroy() to avoid duplication.
Also fixes JSONCacheDestroyable referencing non-existent DefaultJSONWriter
(renamed to StrutsJSONWriter).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Fix Container.destroy(): don't clear factories, don't call from reloadContainer
factories must remain intact because existing code holds direct
references to the Container after destroyConfiguration() and expects
it to still resolve dependencies (e.g. during configuration reload).
reloadContainer() reverted to clearing packageContexts/loadedFileNames
directly — calling destroy() there nulled the container reference and
cleared state needed during the bootstrap transition.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Restore destroy() call in reloadContainer()
The test failures were caused by factories.clear() in
Container.destroy(), not by calling destroy() from reloadContainer().
Now that factories.clear() is removed, destroy() is safe to call
here — it clears packageContexts, loadedFileNames, and the container's
reflection caches in one place.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* WW-5537 Fix Sonar issues: thread-safe FinalizableReferenceQueue, empty method comments
- Replace volatile field with AtomicReference in FinalizableReferenceQueue
for proper thread safety using getAndSet()
- Add comments to empty destroy() implementations in test mocks
- Replace deprecated new URL() with URI.toURL() in DispatcherCleanupTest
- Add comments to empty listener methods in DispatcherCleanupTest
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The duplicate @Action name detection in PackageBasedActionConfigBuilder
was embedded inside a conditional block that only ran when execute() was
NOT annotated with @Action. This meant two methods could map to the same
action name silently when execute() had an @Action annotation, with one
overwriting the other non-deterministically.
Extract the duplicate check to run unconditionally before the conditional
block, so it applies to all annotated methods regardless of whether
execute() is annotated.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The PackageBasedActionConfigBuilder now catches NoClassDefFoundError in
addition to ClassNotFoundException when scanning for action classes.
This prevents application startup failures when classes have missing
optional dependencies (e.g., test classes depending on JUnit).
Changes:
- Add NoClassDefFoundError to catch block in getActionClassTest()
- Improve error message to suggest missing dependencies
- Add unit tests for both exception types
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The exclusion pattern "org.apache.struts2.*" was not properly excluding
classes directly in the root package (like XWorkTestCase) because:
1. PackageBasedActionConfigBuilder extracts package names using
substringBeforeLast(className, ".") which produces "org.apache.struts2"
(no trailing dot)
2. The wildcard pattern requires a literal "." before "*"
3. Result: Pattern doesn't match root package classes
Fix: Enhanced checkExcludePackages() to automatically handle patterns
ending with ".*" by also checking if the package name equals the base
pattern (without ".*").
Now "org.apache.struts2.*" properly excludes both:
- Classes in root package: org.apache.struts2.XWorkTestCase
- Classes in subpackages: org.apache.struts2.dispatcher.SomeClass
Closes [WW-5594](https://issues.apache.org/jira/browse/WW-5594)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* [maven-release-plugin] prepare release STRUTS_7_1_1
* [maven-release-plugin] rollback the release of STRUTS_7_1_1
* [maven-release-plugin] prepare release STRUTS_7_1_1
* [maven-release-plugin] prepare for next development iteration