Advanced Wildcard Mappings Parameters not found in Prepare() method

From the documentation: Struts2's Advanced Wildcard Mappings:

Advanced Wildcards

From 2.1.9+ regular expressions can be defined defined in the action name. To use this form of wild card, the following constants must be set:

<constant name="struts.enable.SlashesInActionNames" value="true"/> 
<constant name="struts.mapper.alwaysSelectFullNamespace" value="false"/>
<constant name="struts.patternMatcher" value="regex" />

The regular expressions can be in two forms, the simplest one is {FIELD_NAME}, in which case the field with the FIELD_NAME in the action will be populated with the matched text, for example:

<package name="books" extends="struts-default" namespace="/">
    <action name="/{type}/content/{title}" class="example.BookAction">
        <result>/books/content.jsp</result>
    </action> 
</package>

In this example, if the url /fiction/content/Frankenstein is requested, BookAction's field "type" will be set to "fiction", and the field "title" will be set to "Frankenstein".

This is absolutely great, and works fine if you read those variables in a regular Action method, like execute().

If you try to read them from the prepare() method, they are null, because the PrepareInterceptor runs before the other Interceptors responsibles for setting the parameters; the usual way to resolve this issue is to use the apposite Interceptor Stack to get the parameters already populated when executing the prepare() method...

From the documentation: ParamsPrepareParamStack

<!-- An example of the paramsPrepareParams trick. This stack
     is exactly the same as the defaultStack, except that it
     includes one extra interceptor before the prepare interceptor:
     the params interceptor.

     This is useful for when you wish to apply parameters directly
     to an object that you wish to load externally (such as a DAO
     or database or service layer), but can't load that object
     until at least the ID parameter has been loaded. By loading
     the parameters twice, you can retrieve the object in the
     prepare() method, allowing the second params interceptor to
     apply the values on the object. -->

This works great for parameters coming from the page, but it does not work for the parameters set by Advanced Wildcards. They are still null.

How to resolve this issue ?


Solution 1:

That parameters are not set by the ParametersInterceptor (like those coming from the JSP), but by the StaticParametersInterceptor.
To have them filled in the prepare() method, the same trick of the paramsPrepareParamsStack must be applied.
Since there is not an Interceptor Stack that does that out-of-the-box, you must define it.
Starting from the defaultStack, we should create a Stack like this:

<interceptor-stack name="allYourParamsAreBelongToUsStack">
    <interceptor-ref name="exception"/>
    <interceptor-ref name="alias"/>
    <interceptor-ref name="servletConfig"/>
    <interceptor-ref name="i18n"/>
 <!-- THE TRICK: NOW PREPARE() WILL FIND EVERYTHING SET -->     
    <interceptor-ref name="staticParams"/>
    <interceptor-ref name="actionMappingParams"/>
    <interceptor-ref name="params">
        <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
    </interceptor-ref>
 <!-- END OF THE TRICK -->
    <interceptor-ref name="prepare"/>
    <interceptor-ref name="chain"/>
    <interceptor-ref name="scopedModelDriven"/>
    <interceptor-ref name="modelDriven"/>
    <interceptor-ref name="fileUpload"/>
    <interceptor-ref name="checkbox"/>
    <interceptor-ref name="multiselect"/>
    <interceptor-ref name="staticParams"/>
    <interceptor-ref name="actionMappingParams"/>
    <interceptor-ref name="params">
        <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
    </interceptor-ref>
    <interceptor-ref name="conversionError"/>
    <interceptor-ref name="validation">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
    <interceptor-ref name="workflow">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
    <interceptor-ref name="debugging"/>
</interceptor-stack>

Note: ActionMappingParams is not needed, just included for future uses.

Please comment in case you discover any problem related to this. AFAIK, it works flawlessly.