javastruts2strutsurl-mappingwildcard-mapping

Why action mapping "*_*" works just like "*"?


There are two actions and the *_* is in front of **(it doesn't matter at all that the default namespace does its work after specific namespace(unless the specific namespace extends it))

<package name="default" namespace="" extends="struts-default"></package>
<package name="admin" namespace="/admin" extends="default">
    <global-results>
       <result>/admin/result.jsp</result>
    </global-results>
    
    <action name="login" class="org.cc.action.AdminAction">
       <result>/admin/admin.jsp</result>
    </action>
    <action name="*_*" class=org.cc.action.{1}Action" method="{2}">
    </action>
</package>

<package name="error" namespace="" extends="default">
   <action name="**" >
     <result>/error.jsp</result>
   </action>
</package>

There is a error page I defined for catching the action that doesn't exist

Then I tested the action like

localhost:8080/myProjectName/admin/AdminAction.action

(it doesn't exist)

But it was caught by the action named "*_*" because an exception showed that AdminActionAction.class was not found.

Then I used the *__*(there are two '_'). It worked well and showed the error.jsp.

the format like "*a*" or "*#*" didn't work either that they would catch any action even if there is no a and # in the name of the action.

I use Struts 2.3.4.

Is there any document said between * and * there must be at least two characters or it will be just like *?

and here is the detail of the exception

Unable to instantiate Action, org.cc.action.AdminActionAction,  defined for     'AdminAction' in namespace '/admin'org.cc.action.AdminActionAction
com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:319)
com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:400)
com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:194)
org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63)
org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:39)
com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58)
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:501)
org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)


java.lang.ClassNotFoundException: org.cc.action.AdminActionAction
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1711)
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1556)
com.opensymphony.xwork2.util.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:152)
com.opensymphony.xwork2.ObjectFactory.getClassInstance(ObjectFactory.java:108)
com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:161)
com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:151)
com.opensymphony.xwork2.ObjectFactory.buildAction(ObjectFactory.java:121)
com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:300)
com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:400)
com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:194)
org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63)
org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:39)
com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58)
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:501)
org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)

and it is really easy to see that

localhost:8080/myProjectName/admin/AdminAction.action

(it doesn't exist) was caught by

<action name="*_*" class="net.org.cc.{1}Action" method="{2}">

Solution

  • In the configuration above mapping was created with action name AdminAction and namespace /admin. That's why you'd gotten an exception because action config matchers found an action config for the patten "*_*". But it can't find it for the pattern "*__*" for the same action name. Thus fallback to the default namespace "" and in this namespace it matches "**" pattern as a result to error page.

    Actually for this action name created two similar matcher and one of them did match. What the matcher is created and what pattern is used?

    The default runtime comfiguration implementation uses the parameter looseMatch that is hardcoded to true. There's desctiption in the javadoc

    Patterns can optionally be matched "loosely". When the end of the pattern matches *[^*]*$ (wildcard, no wildcard, wildcard), if the pattern fails, it is also matched as if the last two characters didn't exist. The goal is to support the legacy "*!*" syntax, where the "!*" is optional.

    And as @Quaternion mentioned in the comment, the matcher with paterrn * added to the list. Anything else as described earlier.