tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Erik Hatcher <e...@ehatchersolutions.com>
Subject dynamic library
Date Tue, 23 Dec 2003 19:44:28 GMT
In my efforts to design a dynamic front-end where the UI is built from 
a database-driven specification of what fields should be presented, I 
am exploring the possibility of using a completely dynamic "library" 
(in the Tapestry sense) which serves up dynamically defined 
"components" (again in the Tapestry sense).  I'm happy to report that 
it is working!

In part of my explanation, I'll be complaining about some pieces of 
Tapestry, but I first want to again thank Howard and the other Tapestry 
developers that built this thing - it has proven to be extremely 
flexible and powerful.

I'm going to describe what I have done, in order to benefit others, but 
also to solicit feedback on ways to improve and build upon what I have 
done.  In fact, this dynamic library could be made into something 
worthy of contributing back to Tapestry.

 From a custom BaseEngine extension, I already had an overridden 
createSpecificationSource providing a custom subclass of 
DefaultSpecificationSource; from there, I overrode 
getLibrarySpecification.  If the library name is "dyna.library", I 
returned a custom DynaLibrarySpecification, otherwise delegated to the 
super class to carry on as normal.

In my .application file I added <library id="dyna" 
specification-path="dyna.library"/>, and the path of course is 
arbitrary and keyed off of in getLibrarySpecification above.  Maybe 
there is a way to even more dynamically add a library to an 
application?  If so, how? (I didn't even explore this route yet, but 
perhaps I can do away with some code if done implicitly).

My DynaLibrarySpecification is a bare-bones implementation of 
ILibrarySpecification directly - with most methods doing nothing or 
returning null.  This brings me to my first complaint.  In digging into 
extending Tapestry in sophisticated ways, it is great there are 
interfaces for everything, but in several cases the interfaces have far 
too many methods on them.  For example, in this case, 
ILibrarySpecification has methods really only used by 
LibrarySpecification for populating via Digester.  It seems to me that 
only methods for retrieving information from a library really belong on 
the interface (or action methods, of course), but setter/adder methods 
do not belong there.  These setter/adder methods really only belong on 
the implementation that is designed to be populated from an XML file.  
Am I correct that methods like setDescription really aren't needed on 
the interface?  (in fact, nothing specifically calls that except for 
Digester currently).

One other complaint - there are lots of places where a List is 
returned.  Yes, it is explicitly documented that these should return 
empty lists, not null, but why not allow null to be returned?  (this is 
not a big deal, just a minor nit causing me more work to implement a 
bunch of otherwise dummy methods).

I still have not quite mastered exactly how to use IResourceLocation, 
but the only real reason to implement my own ILibrarySpecification was 
to return a custom IResourceLocation, DynaResourceLocation in this 
case, from getSpecificationLocation.  I had to make 
DynaResourceLocation a bit more clever than it seems necessary.... when 
using an @Insert component from a dynamically generated template, for 
example, the framework calls to construct a DynaResourceLocation using 
getRelativeLocation - which seemed odd to me so I first tried returning 
null, but that causes an NPE elsewhere in the framework, so I had to 
move some smarts to getResourceURL to return null if it is not a 
recognized component (this causes the framework to search further and 
find @Insert in the framework namespace).

No back to my custom subclass of DefaultSpecificationSource - I 
overrode getComponentSpecification and if the location is an instanceof 
my DynaResourceLocation I return back a custom 
DynaComponentSpecification, which is a raw implementation of 
IComponentSpecification.  Once again, all of the setters here... 
geez!!!!  For my prototyping, I just returned the class name of my 
DynaComponent (an empty BaseComponent subclass) from 
getComponentClassName.  To add a parameter to my dynamic components, I 
just hacked a quick and dirty implementation of getParameter with a 
mostly empty IParameterSpecification (once again, those darn setters on 
the interface!) and getParameterNames.

Wait, but that is not all... since my dynamic component extends from 
BaseComponent, it needs a template.  So my custom engine overrides 
createTemplateSource and returns custom subclass of 
DefaultTemplateSource.  For prototyping purposes, I just did a quick 
check on the "dyna" namespace in an overridden getTemplate method and 
hacked a template in a String, then parsed it just like 
DefaultTemplateSource does (this required some cut and paste of the 
ParserDelegate from DefaultTemplateSource since it was private).

Voila!

In a test page, I put this:

	<dyna:FooBar attribute="blah"/>

There is no FooBar.html or FooBar.jwc.  The "attribute" is added 
dynamically through the component specification I mentioned above (with 
getParameterNames and getParameter).  The template data is a String:

	String template = "<b>FooBar!!!!  <span key=\"someKey\"/>  <span 
jwcid=\"@Insert\" value=\"ognl:attribute\"/>";

I added the <span key="..."/> in just to see how message resources 
worked, which required me to update my custom subclass of 
DefaultComponentMessagesSource to handle the "dyna" namespace by 
passing through to our database rather than looking for a 
FooBar.properties file.  And the @Insert forced me to do some things 
with DynaResourceLocation, which I've already mentioned.

Voila!  Dynamically generated components and templates!

I'm quite impressed that (despite how complex it looks above) it was 
not too bad to implement this.  Do folks think this is something that 
should be added into Tapestry itself somehow?  To be honest, I'm not 
sure how to package it up to be generically useful, and the core 
already supports this dynamic stuff - but having an easier way to have 
a non-file-based template/specification would be cool.  I've currently 
only focused on components, but the same could easily be done with 
pages.

But, now the questions continue.... how do I go about utilizing these 
dynamic components, um, "dynamically"??   Do I need to build a dynamic 
page as well?  But how do you add components to it dynamically?  (I 
haven't tried, so maybe this is not too hard either).  I could, I 
suppose, create an HTML template for a page dynamically and parse it.  
I'm looking for how to take this further.  Here is the use case - I'm 
going to have a database tell me what elements to present - each of 
these will correspond to a component type.  How do I do an @Foreach 
over all elements and inject these components on the fly?

	Erik


---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org


Mime
View raw message