You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tiles.apache.org by Mck <mi...@wever.org> on 2010/02/09 13:40:52 UTC

howto delegate to other definitions ?

I'm trying to insert other definitions.
But the attributes from the other definition are never available.

My tiles.xml


    <definition name="WILDCARD:*.*" extends="{1}" template="/WEB-INF/tiles/templates/{2}_template.jsp">
        <put-attribute name="vertical" value="vertical.{2}" cascade="true" type="definition"/>
        <put-attribute name="body" value="/WEB-INF/tiles/vertical/{2}/content_new.jsp" cascade="true"/>
    </definition>
    <definition name="vertical.motor" template="/WEB-INF/blank.jsp">
        <put-attribute name="description" value="/WEB-INF/tiles/vertical/motor/description.jsp" cascade="true"/>
    </definition>

The explicit definition i use here is "advert.motor.car"
so my template is /WEB-INF/tiles/templates/motor_template.jsp
and looks like

<html>
<body>
 <tiles:insertAttribute name="body"/>
 <tiles:insertDefinition name="vertical.motor">
    <%-- the following does not work --%>
    <tiles:insertAttribute name="description"/>
 </tiles:insertDefinition>
</body>
</html>


This other definition "vertical.motor" has only a blank template because
all i need are access its inner attributes.

But all i get is 
"NoSuchAttributeException: Attribute 'description' not found."

~mck

-- 
"Microsoft bought MS-DOS from a Seattle company, and it was called QDOS
then (Quick and Dirty Operating System). Some say it is not quick
anymore, but the rest stays the same." WILSON ROBERTO AFONSO 
| semb.wever.org | sesat.no | finn.no |


Re: howto delegate to other definitions ?

Posted by Antonio Petrelli <an...@gmail.com>.
2010/2/9 Mck <mi...@wever.org>:
>    <definition name="WILDCARD:*.*" extends="{1}" template="/WEB-INF/tiles/templates/{2}_template.jsp">
>        <put-attribute name="vertical" value="vertical.{2}" cascade="true" type="definition"/>
>        <put-attribute name="body" value="/WEB-INF/tiles/vertical/{2}/content_new.jsp" cascade="true"/>
>    </definition>
>    <definition name="vertical.motor" template="/WEB-INF/blank.jsp">
>        <put-attribute name="description" value="/WEB-INF/tiles/vertical/motor/description.jsp" cascade="true"/>
>    </definition>
>
> The explicit definition i use here is "advert.motor.car"

And why not defining such an explicit definition directly? It seems
the best option to me.

Antonio

Re: howto delegate to other definitions ?

Posted by Antonio Petrelli <an...@gmail.com>.
2010/2/16 Mck <mi...@semb.wever.org>:
> I'm not sure yet, since there's some hacking both ways

I don't think this is a complete "hack", since accessing to the parent
(or ancestor) attribute context could be an interesting feature. Tiles
simply is missing that :-D
If you have time to try it, and it works and you like it, you could
open a JIRA issue for this (contributions are welcome too).

Ciao
Antonio

Re: howto delegate to other definitions ?

Posted by Mck <mi...@semb.wever.org>.
> Associate a preparer to vertical.motor that accesses to the stack of 
> AttributeContext's: 
> 
>  ArrayStack<AttributeContext> contextStack = 
>             (ArrayStack<AttributeContext>) tilesContext 
>                 .getRequestScope().get(ATTRIBUTE_CONTEXT_STACK); 
> 
> Where ATTRIBUTE_CONTEXT_STACK has this value: 
> "org.apache.tiles.AttributeContext.STACK" 
> 
> Get the element under the top of the stack, that it is the attribute 
> context of the parent definition (probably, it might be 2 under the 
> top, not sure) and transfer the attribute that is under the current 
> AttributeContext (parameter in the preparer).
> ...
> Does it sound better? :-D

I'm not sure yet, since there's some hacking both ways (overriding
getDefinitions() is cleaner), but it definitely opens up my
understanding of what can be possible. thank you. The most important
thing here is to find the solution which scales clean and neat the best
so i'll experiment some more with both ideas.

~mck

Re: howto delegate to other definitions ?

Posted by Mck <mi...@wever.org>.
> >    <definition name="WILDCARD:*.*" extends="{1}" template="/WEB-INF/tiles/templates/{2}_template.jsp"> 
> >        <put-attribute name="vertical" value="vertical.{2}" cascade="true" type="definition"/> 
> >        <put-attribute name="body" value="/WEB-INF/tiles/vertical/{2}/content_new.jsp" cascade="true"/> 
> >    </definition> 
> >    <definition name="vertical.motor" template="/WEB-INF/blank.jsp"> 
> >        <put-attribute name="description" value="/WEB-INF/tiles/vertical/motor/description.jsp" cascade="true"/> 
> >    </definition> 
> > 
> > The explicit definition i use here is "advert.motor.car" 
> 
> And why not defining such an explicit definition directly? It seems 
> the best option to me.

1) because of the overwhelming verbosity, and
2) the need to "lock-down" the core hierarchy of definitions,
3) migration of a legacy application where "organisation-and-tidy"
weighs heavier than the "ideal-solution-to-begin-with".

The core hierarchy
   "WILDCARD:*.*.*" --> "WILDCARD:*.*" --> "WILDCARD:*"

along with the fallback syntax "[{3}|{2}|{1}]" gives a solution that
most pages can use without modification to any of the definitions.

It's a pluggable-solution, just drop a custom jsp into the car/ folder.

For example given the following jsps exist used to generate a general
"advert.motor" page:
 /WEB-INF/tiles/vertical/motor/body.jsp
 /WEB-INF/tiles/vertical/motor/top_menu.jsp
 /WEB-INF/tiles/vertical/motor/left_menu.jsp
 /WEB-INF/tiles/vertical/motor/content.jsp
 /WEB-INF/tiles/vertical/motor/right_column.jsp
 /WEB-INF/tiles/vertical/motor/footer.jsp

And one of the developers, that doesn't have permission to edit the core
tiles.xml file, wants to build a "advert.motor.truck" page. All they
have to do is provide the overriding jsps:
 /WEB-INF/tiles/vertical/trunk/content.jsp
 /WEB-INF/tiles/vertical/trunk/right_column.jsp
and they have the page up and running.

But then say, they wish to do more tiling inside content.jsp, a typical
use-case for a nested definition. The developer doesn't have permission
to edit the core tiles.xml, and putting a specific trunk attribute into
the "WILDCARD:*.*.*" would just be messy for all the other developers
not caring about trucks, so the sideways-delegation is introduced. Here
they can just add, into their own private tiles-motor.xml, the
"subvertical.truck" definition and the attributes that will be used
inside .../trunk/content.jsp

All-in-all this keeps developers out of the definition files as long as
possible, and even then keeps them in their own private definition file.
The alternative forces huge verbosity and duplication between teams of
developers, with no doubt frequent edits on definitions.

Make sense?

~mck

-- 
We are born naked, wet and hungry. Then things get worse. 
| semb.wever.org | sesat.no | finn.no |

Re: howto delegate to other definitions ?

Posted by Mck <mi...@wever.org>.
> First of all, version of Tiles? 

I'm working with trunk (2.2.2-SNAPSHOT), via Spring-web
(3.0.1-SNAPSHOT), made possible with the patch included in
http://jira.springframework.org/browse/SPR-6097

Thanks for a quick response.

> >    <definition name="vertical.motor" template="/WEB-INF/blank.jsp"> 
> >        <put-attribute name="description" value="/WEB-INF/tiles/vertical/motor/description.jsp" cascade="true"/> 
> >    </definition> 
> >... 
> 
> The description attribute is available from "vertical.motor" 
> definition to all *included* definitions, not definitions that include 
> it. 

I'm starting to see that. Was hoping that insertDefinition, or
importAttribute (given that the attribute is a definition), would make
it possible to access the internal attributes from a different
definition.

> I suppose that to accomplish what you want to do is using a preparer. 

That doesn't quite fit the big picture for me :-(
What i'm after is delegation, or injection, of definitions in addition
to the supported extension.

To explain, i got is a number of "global" wildcarded definitions like this:

    <!-- sub-vertical definition -->
    <definition name="WILDCARD:*.*.*" extends="{1}.{2}" template="/WEB-INF/tiles/templates/[{3}|{2}|{1}]_template.jsp">
        <put-attribute name="body" value="/WEB-INF/tiles/vertical/[{3}|{2}|{1}]/content_new.jsp" cascade="true"/>
        ...        
        <!-- definition injection -->
        <put-list-attribute name="definition-injection" inherit="true">
            <add-attribute value="subvertical.{3}" type="string"/>
        </put-list-attribute>
    </definition>

    <!-- vertical definition -->
    <definition name="WILDCARD:*.*" extends="{1}" template="/WEB-INF/tiles/templates/[{2}|{1}]_template.jsp">
        <put-attribute name="body" value="/WEB-INF/tiles/vertical/[{2}|{1}]/content_new.jsp" cascade="true"/>
        ...
        <!-- Dynamic definition injection -->
        <put-list-attribute name="definition-injection">
            <add-attribute value="vertical.{2}" type="string"/>
        </put-list-attribute>
    </definition>

Then in additional tiles-*.xml (that individual developer teams are free to edit) definitions like:

    <!-- motor definition that is injected into the real definition -->
    <definition name="vertical.motor">
        <put-attribute name="motor.description" value="/WEB-INF/tiles/vertical/motor/description.jsp"/>
        <put-attribute name="motor.equipment" value="/WEB-INF/tiles/vertical/motor/equipment.jsp"/>
    </definition>

    <!-- car definition that is injected into the real definition -->
    <definition name="subvertical.car">
        <put-attribute name="car.something" value="/WEB-INF/tiles/vertical/car/something.jsp"/>
    </definition>

By overriding the DefinitionsFactory like:

            private static final String DEF_INJECTION = "definition-injection";
            @Override
            public Definition getDefinition(String name, TilesRequestContext tilesContext) {
                Definition def = super.getDefinition(name, tilesContext);
                if(null != def){
                    def = new Definition(super.getDefinition(name, tilesContext));
                    Attribute defList = def.getLocalAttribute(DEF_INJECTION);
                    if(null != defList){
                        if(defList instanceof ListAttribute){
                            List<Attribute> list = (List<Attribute>) defList.getValue();
                            for(Attribute inject : list){
                                injectDefinition((String) inject.getValue(), def, tilesContext);
                            }
                        }
                    }
                }
                return def;
            }
            private void injectDefinition(String fromName, Definition to, TilesRequestContext cxt){
                Definition from = super.getDefinition(fromName, cxt);
                for(String attrName : from.getLocalAttributeNames()){
                    to.putAttribute(attrName, from.getLocalAttribute(attrName), true);
                }
            }

these "definition-injection" definitions are injected at run-time.
For example the spring controller returns a View("advert.motor.car").
This matches the definition with hierarchy:

 "WILDCARD:*.*.*" --> "WILDCARD:*.*" --> "WILDCARD:*"

but each definition has in addition "a sideways delegation" so-to-speak,
like:

 "WILDCARD:*.*.*" --> "subvertical.{3}"
    |
    v
 "WILDCARD:*.*"   --> "vertical.{2}"
    |
    v
 "WILDCARD:*"


I've got this working as explained, but was hoping there was an existing
and easier way to achieve it all. Is there?

~mck

ps the "[{3}|{2}|{1}]" syntax is provided by a custom
TemplateAttributeRenderer, it returns the found template looking first
for {3}, then for {2}, and finally for {1}.


-- 
"People only get lost in thought because it is unfamiliar territory."
Paul Fix 
| semb.wever.org | sesat.no | finn.no |

Re: howto delegate to other definitions ?

Posted by Antonio Petrelli <an...@gmail.com>.
2010/2/9 Mck <mi...@wever.org>:
>    <definition name="WILDCARD:*.*" extends="{1}" template="/WEB-INF/tiles/templates/{2}_template.jsp">
>        <put-attribute name="vertical" value="vertical.{2}" cascade="true" type="definition"/>
>        <put-attribute name="body" value="/WEB-INF/tiles/vertical/{2}/content_new.jsp" cascade="true"/>
>    </definition>
>    <definition name="vertical.motor" template="/WEB-INF/blank.jsp">
>        <put-attribute name="description" value="/WEB-INF/tiles/vertical/motor/description.jsp" cascade="true"/>
>    </definition>

Another try, but this time it involves a bit of hacking and exposing
some Tiles internals.
Associate a preparer to vertical.motor that accesses to the stack of
AttributeContext's:

 ArrayStack<AttributeContext> contextStack =
            (ArrayStack<AttributeContext>) tilesContext
                .getRequestScope().get(ATTRIBUTE_CONTEXT_STACK);

Where ATTRIBUTE_CONTEXT_STACK has this value:
"org.apache.tiles.AttributeContext.STACK"

Get the element under the top of the stack, that it is the attribute
context of the parent definition (probably, it might be 2 under the
top, not sure) and transfer the attribute that is under the current
AttributeContext (parameter in the preparer).
Finally, in your template page do:

>  <tiles:insertDefinition name="vertical.motor" preparer="my.Preparer"/>
>  <tiles:insertAttribute name="description"/>

You can also associate the preparer in the tiles.xml file if you like.

Does it sound better? :-D

Ciao
Antonio

Re: howto delegate to other definitions ?

Posted by Antonio Petrelli <an...@gmail.com>.
First of all, version of Tiles?

2010/2/9 Mck <mi...@wever.org>:
>    <definition name="vertical.motor" template="/WEB-INF/blank.jsp">
>        <put-attribute name="description" value="/WEB-INF/tiles/vertical/motor/description.jsp" cascade="true"/>
>    </definition>
>...
> The explicit definition i use here is "advert.motor.car"
> so my template is /WEB-INF/tiles/templates/motor_template.jsp
> and looks like
>
> <html>
> ...
>    <tiles:insertAttribute name="description"/>

The description attribute is available from "vertical.motor"
definition to all *included* definitions, not definitions that include
it.
I suppose that to accomplish what you want to do is using a preparer.

Antonio