Files
Lukasz Lenart 94a8fcb26c WW-3784 Order annotated wildcard actions most-specific-first (#1813)
* 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>
2026-07-29 07:56:58 +02:00
..
2026-06-26 07:18:26 +02:00

Struts 2 Convention plugin

This plugin allows to use convention over configuration approach instead of configuring everything using struts.xml. You will find more details in documentation.

Installation

Just drop this plugin JAR into WEB-INF/lib folder or add it as a Maven dependency.

Design

Action with no annotations (all the default handlings)

Action name is class name.

Action package is based on the either any Namespace annotations on the class or Java package the action class is defined in or the Java package name and the root Java package, which is determined based on the package locators or the action package list. In either case, a root package is identified and the namespace is the packages below the root up to and including the package that contains the action class.

Action method is execute in all cases.

Results are built first from the Result annotations at the class level. Next, they are built from the resources available in the web application and in the classpath based on the result location, namespace of the action and action name (not anything to do with the code element names). Results are found for common result codes including, success, error, input, failure, etc. If a specific result doesn't exist using the result code then just the action namespace and action name are used to locate results. If there are still no results found, than a redirect is added back to the index action for the namespace, if one exists.

Action with Action(s) annotations

If the annotation is located on any method other than execute, than the action configuration built from the annotation is in addition to the action configuration built using the default. Unless there is an annotation that contains no value, in which case, the method that annotation is defined for becomes the default action method. If the annotation is defined on the execute method, regardless of the value of the annotation, no default configuration is built for the class.

If multiple annotations have no value, an error should occur. If multiple annotations have the same value, an error should occur.

Action name is taken from the annotation value. If the value is empty, the default action name is used.

Action package is taken from the annotation value. If the annotation value doesn't start with a slash character, the default action namespace is used.

Action method is always the method that the annotation is defined on. Results are built first from the array of Results on the Action annotation. Next, they are built from the class level Result annotations. Lastly, all default results are created. All Results already created during this process should not be overridden later in the process.