1

I am still confused how to properly use the namespace attribute in Struts2.

In the namespace configuration, it was mentioned that:

Namespaces are not a path!

Namespace are not hierarchical like a file system path. There is one namespace level. For example if the URL /barspace/myspace/bar.action is requested, the framework will first look for namespace /barspace/myspace. If the action does not exist at /barspace/myspace, the search will immediately fall back to the default namespace "". The framework will not parse the namespace into a series of "folders". In the Namespace Example, the bar action in the default namespace would be selected.

I have tried making a simple Struts2 sample:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 
                        "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.devMode" value="true" />

    <package name="default" extends="struts-default">
        <action name="defaultIndex">
            <result name="success">/pages/default.jsp</result>
        </action>
    </package>

    <package name="package1" namespace="/" extends="struts-default">
        <action name="index1">
            <result name="success">/pages/home1.jsp</result>
        </action>
    </package>

    <package name="package2" namespace="/namespace1" extends="struts-default">
        <action name="index2">
            <result name="success">/pages/home2.jsp</result>
        </action>   
    </package>

    <package name="package3" namespace="/namespace1/namespace2" extends="struts-default">
        <action name="index3">
            <result name="success">/pages/home3.jsp</result>
        </action>
    </package>
</struts>

Where SampleDynamicWebProject is the context root.

Based on the documentation, if I try

.../SampleDynamicWebProject/randomText/defaultIndex

Then, Struts2 will look for the /randomText namespace and check for the defaultIndex action. If it doesn't exist, then it will look for the default namespace which is the package with no namespace attribute.

But if I try this URL:

.../SampleDynamicWebProject/namespace1/randomText/index2

Struts2 should look at the /namespace1/randomText namespace for the index2 action and if it cannot see one, then it should look at the default namespace. However, the URL above is still directed at the index2 action in the /namespace1.

The same thing is happening when I try

.../SampleDynamicWebProject/randomText/index1

The index1 in the root namespace is invoked.

Can you please clarify how exactly it works?

Community
  • 1
  • 1
jngacayan
  • 55
  • 10
  • What answer do you expect? It is how S2 works. You can tune this behavior with `struts.mapper.alwaysSelectFullNamespace` and `struts.enable.SlashesInActionNames` constants. – Aleksandr M Nov 10 '15 at 09:13
  • I consider this the best first question ever seen. I disagree on the comment @AleksandrM, nowhere in the documentation is mentioned that it should work like that; the question seems absolutely legit to me – Andrea Ligios Nov 10 '15 at 09:46
  • @AndreaLigios: Not saying that this is not legit question. Just not sure what answers OP expecting. – Aleksandr M Nov 10 '15 at 10:35
  • @AleksandrM I know but this behavior is not described in the docs, and I remember having the same doubts years ago. It is a real hole in the docs, IMHO. WHY namespace1/randomText -> namespace1 is found ? That's the question, that's not only not immediate, but definitely misleading. It should match or break, not match every URI with garbage in the middle, that instead is what is done, without being even documented – Andrea Ligios Nov 10 '15 at 10:40
  • Yeah, it comes up all the time, e.g., http://stackoverflow.com/q/8424215/438992, http://stackoverflow.com/q/17690956/438992, and maybe more. I meant to do something about it. – Dave Newton Nov 11 '15 at 22:14

1 Answers1

1

You're definitely right, nowhere in the documentation is mentioned that if a namespace is part of the URL, it is enough to make the whole thing work.

But it does, and here is how it works in detail:

To parse an URL and call the appropriate namespace and action, Struts2 uses an ActionMapper:

The ActionMapper interface provides a mapping between HTTP requests and action invocation requests and vice-versa.

There are different implementations, but the default one is the DefaultActionMapper.

When an URL is analyzed, the parseNameAndNamespace() method is invoked. If it's not the default "" nor the root "/" one, and the alwaysSelectFullNamespace attribute is set to false (that is the default), then the available namespaces are scanned:

The magic is in this line:

if (ns != null 
 && prefix.startsWith(ns) 
 && (prefix.length() ==ns.length() || prefix.charAt(ns.length()) == '/')) {
    if (ns.length() > namespace.length()) {
        namespace = ns;
    }
   ///... 
}

name = uri.substring(namespace.length() + 1);

In your example:

/namespace1/randomText/index2

prefix = /namespace1/randomText/
ns     = /namespace1

In your case it is not null, the URI starts with the namespace, the length is not the same BUT the character after the /namespace1 String in the URI is a slash, then the action mapper decides that:

namespace = /namespace1
action    = randomText/index2

This is why the namespace is taken. But then why it works, since the action name is index2 and not randomText/index2 ?

The second magic is in this other line:

if (!allowSlashesInActionNames) {
    int pos = name.lastIndexOf('/');
    if (pos > -1 && pos < name.length() - 1) {
        name = name.substring(pos + 1);
    }
}

If the allowSlashesInActionNames attribute is set to false (that is the default) and the action contains slashes (like in randomText/index2), then strips everything up to the last slash, transforming randomText/index2 in index2.

Then as long as you have an URI that is starting with a namespace and ending with an action, no matter what's in the middle, it works.

It should not work instead using /randomText/namespace1/index2, because no namespace is starting with /randomText.

Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243
  • 1
    Thanks! This definitely answered my question and you gave me options as well on how to handle this namespace behavior in struts2. – jngacayan Nov 11 '15 at 07:16