[long] Why building Spindle is hard Pt.4
Geoff Longman <[email protected]> Mon, 13 Feb 2006 00:52:11 -0500
| Newsgroups | gmane.comp.ide.eclipse.spindle.devel |
|---|---|
| Message-ID | <[email protected]> |
Let's summarize what we have so far.
Tapestry applications consist of a hierachy of namespaces. It is
possible for pages/components to exist in more than one namespace.
There are side effects of this 'feature' and although it, rarely,
affects those who develop applications in Tapestry, it is my
contention that it makes building tools for Tapestry very difficult.
BTW I have now decided to use the phrase 'boundary crossing' or
'crossing namespace boundaries' when refering to situations that cause
pages/components to exist in more than one namespace.
In the current incarnation of Spindle some implementation choices were
made. These decisions were to impose conditions that eliminate most
situations where boundary crossing could occur and to ignore the rest.
Now, how does Tapestry 4 make the previous decisions invalid and how
does that make building tools that support Tapestry 4 really hard..
To continue I must describe the SECOND THING that makes building tools
for Tapestry 4 hard.
"Tapestry 4 changed the notion of a page/component 'name'."
In all versions of Tapestry up to and including 3.0.3 a page or
component name was a simple identifier. In Tapestry 4 a page name is a
path + a simple identifier.
MyComponent (a legal component identifier in Tapestry 3 & Tapestry 4)
/a/path/to/MyComponent (a legal component identifier in Tapestry 4 only)
In Tapestry 3&4 the identifier is used at runtime to build a
specification object which is the essence of a Tapestry component. The
specification object is the 'blueprint' used to instantiate the
component. The name of the component class is stored in the
specification object and the template(s) of the component were either
explicitly declared in the specification ($template asset) or looked
up as a permutation of the component's identifier.
The specification object is built by parsing a .jwc xml file (.jwc
files are required in Tapestry 3 and are optional in Tapestry 4. In
Tapestry 4 a synthetic specification object is created if there is no
.jwc file).
In Tapestry 3 it was not required that the path to the .jwc file be
explicity declared in the .application xml file. If the path was not
explitly declared, Tapestry used a set of 'lookup rules' to find the
.jwc file. Here are the (more complex) rules for finding a component
specification in an application namespace...
Look for (in turn):
MyComponent.jwc in the same folder as the application specification
MyComponent.jwc in the WEB-INF/servlet-name directory of the context root
MyComponent.jwc in WEB-INF
MyComponent.jwc in the application root (within the context root)
Ok, there is a fixed set of 'places' where the .jwc file can exist and
it appears that they do not overlap.
Recall that Spindle has a very static view of the project unlike
Tapestry's completely dynamic view. On the surface it may appear that
the static nature of a Spindle project would not jive. Spindle must
build a namespace without any of the information Tapestry has a
runtime. In particular a direct reference to MyComponent.
That's ok though, In Tapestry 3, the name is a simple identifier and
the rules above have a fixed order so for the component named
MyComponent, none of the locations in the look up rules overlap.
To build and validate a namespace's components, Spindle collects up
all the .jwc files using the same rules.
(I'm going to use Spindle core api here. hope I don't lose anyone).
IResourceRoot context = "the context root, ie the parent of WEB-INF"
//the 'rule' locations
ICoreResource appLocation = context.getRelativeResource("whatever the
location is");
ICoreResource webinf = context.getRelativeLocation("/WEB-INF/");
ICoreResource servlet = webInf.getRelativeLocation("servlet-name/");
ICoreResource root = context.getRelativeLocation("/");
//when passed a location, keep it if it is a .jwc file
TapestryResourceLocationAcceptor acceptor
= new TapestryResourceLocationAcceptor(
"*",false, TapestryResourceLocationAcceptor.ACCEPT_JWC);
HashSet<ICoreResource> allJwcFilesForNamespace = new HashSet<ICoreResource>();
appLocation.accept(acceptor);
allJwcFilesForNamespace.addAll(acceptor.getResults());
servlet.accept(acceptor);
allJwcFilesForNamespace.addAll(acceptor.getResults());
webinf.accept(acceptor);
allJwcFilesForNamespace.addAll(acceptor.getResults());
root.accept(acceptor);
allJwcFilesForNamespace.addAll(acceptor.getResults());
So we visit each lookup folder in turn adding each .jwc file we find
to the set. There is some post processing I'm leaving out but it's not
germain to the discussion. Just know that I guarantee, just like
Tapestry guarantees, that "MyComponent.jwc" is only found once.
MyComponent is found in the first loc, OR the second, OR the third,
etc. Since the string MyComponent is a simple identifer, if we did
find /WEB-INF/MyComponent.jwc and /MyComponent.jwc we *know* they
refer to different files and that, due to the rules, /MyComponent.jwc
would never be found at runtime and so we can discard it and mark an
error condition on that file ("Tapestry would never see this file at
runtime").
Given a reference MyComponent, Tapestry does things like
webinf.getRelativeResource("MyComponent.jwc").
Spindle does not have a reference to MyComponent, rather Spindle wants
to collect up all the legally placed .jwc files and validate them all
as if they were referenced. Which is cool as it means new components,
that are not yet referenced, can also be validated.
This works great, in Tapestry 3 projects. It works because component
names are simple identifiers and none of the 'lookup' locations
overlap.
However it breaks down completely in Tapestry 4 projects.
At runtime in Tapestry 4, given "/a/path/to/MyComponent" Tapestry runs
the lookup very same lookup rules.
look at these two rules:
servlet.getRelativeResource("/a/path/to/MyComponent.jwc");
webinf.getRelativeResource("/a/path/to/MyComponent.jwc");
which is the same as:
"/WEB-INF/servlet-name/a/path/to/MyComponent.jwc"
"/WEB-INF/a/path/to/MyComponent.jwc"
it appears that the lookup rules still do not overlap so it appears
that the Spindle pcode above would still work.
But, the identifier for the Component is now path based and it is
really easy for, at runtime, a user to refer to the same component
using to different paths.
Consider that this file exists:
/WEB-INF/servlet-name/a/path/to/MyComponent.jwc
user refers to the Component by "a/path/to/MyComponent", at runtime
the servlet-name rule will match and Tapestry will stop looking.
use can refer to the SAME component as
"servlet-name/a/path/to/MyComponent" and Tapestry is still happy as
the webinf rule matches.
So there are now 2 ways to refer to the exact same component. It
should be noted that before I raised a stink with
https://issues.apache.org/jira/browse/TAPESTRY-724 there was
automatically 3 ways to refer to my component because:
/WEB-INF/servlet-name/MyComponent
was a legal name - Howard made it illegal to have names that include "WEB-INF".
But there can still be the case that there are 3 legal ways to refer
to MyComponent.
say the application specification exists and is located at:
/WEB-INF/servlet-name/spec/servlet-name.application
and MyComponent.jwc is here:
/WEB-INF/servlet-name/spec/MyComponent.jwc
now the following are all legal references to MyComponent.jwc:
MyComponent
spec/MyComponent
servlet-name/spec/MyComponent
a different lookup rule would match for each.
Since Spindle has no knowledge of how an end user will refer to
MyComponent.jwc, I guess Spindle will have to 'install' all 3 names in
the namespace, each referring to the same .jwc file.
It is not really an "aliasing" as Tapestry does not recognize these 3
namings as the same component. It installs a new entry in the
namespace whenever a new component is reference even though they are
the same component. One great point is that behind the scenes tapestry
is very effcient and will only parse an xml file once.
I think this problem only applies to components that have jwc files. I
suppose Spindle could do the same, figuring out all the possible
"names" and installing them into it's namespace object.
So while this is the SECOND THING - perhaps it is surmountable. I
think though that this in combination with THIRD THING coming up there
will be difficulties.
And that's the crappy part. Each problem in isolation is tough but
usually ok. It's in combination that things fall apart.
In Summary
Tapestry 4 kept the same lookup rules but changed the way Components
are named. This change causes the Tapestry runtime to resolve up to 3
different names to the same component and that is the SECOND THING
that makes building tooling for Tapestry 4 tough.
Up next, the THIRD THING.
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems? Stop! Download the new AJAX search engine that makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid3432&bid#0486&dat1642