ResourceHelper -- uniform resource (file) resolution

0 views
Skip to first unread message

Andrew Wills

unread,
Nov 19, 2008, 1:07:32 PM11/19/08
to cernunnos-discussion
Hey folks,

I'm working on something I've had in mind for some time, and I know
I've spoken face-to-face with some of you about it...

As I see it, there are 2 important limitations to the way resources
(files) are referenced currently:
- (1) each Task or Phrase has its own implementation, and different
objects do it differently
- (2) our current custom URLStreamHandlers (classpath:/ and C:/)
don't work in webapps

I'm planning to implement a class called ResourceHelper -- essentially
modeled off of Eric's CacheHelper -- that will take over
responsibility for resolving resources. I believe it will
(ultimately) overcome both these issues nicely.

Issue #1
------------
Take a look at ${doc} vs. <spring-beans>:
- both use CONTEXT and LOCATION reagents and support defaults for
both reagents
- both accept Attributes.LOCATION as the default LOCATION (if none
is specified)
- ${doc} uses the directory where Java is executing as the default CONTEXT
- <spring-beans> uses Attributes.ORIGIN as the default CONTEXT

This is just one small (but common) example; some components use
Attributes.CONTEXT as a default CONTEXT, and some prefer
Attributes.CONTEXT (if present) but will use Attributes.ORIGIN
otherwise. These differences can be confusing.

Issue #2
------------
The current approach to custom URLStreamHandlers is based on setting
the JVM URLStreamHandlerFactory. But this object can only be set once
per JVM, and (surprise) Tomcat has already done it by the time your
webapp code starts executing. So in webapps that use CRN, there's no
support for the 'classpath:/' protocol.

ResourceHelper
-----------------------
The ResourceHelper approach allows us to nail both of these thorny items.

For Issue #2, it allows it to apply a custom URLStreamHandler without
resorting to a URLStreamHandlerFactory.

Issue #1 is a bit trickier: the ResourceHelper makes resource
resolution uniform for all Tasks and Phrases that use it. But (I feel
that) we can't just apply it to all existing tasks and phrases
indiscriminately, because that would change the behavior of those
components and break backwards compatibility. Instead I plan to apply
it to existing components where I can, but deprecate and replace other
components where I can't.

So, for example,I could replace ${doc} with a new ${parseXml} phrase
that uses the ResourceHelper. ${doc} would still be around (at least
for a while longer), behaving the same as ever.

Here's how I think ResourceHelper should work:
- there should be a LOCATION reagent that defaults to Attributes.LOCATION
- there should be a CONTEXT reagent that defaults to
Attributes.CONTEXT (if present) or Attributes.ORIGIN (if not)

I expect to apply these enhancements to the 1.1.x line (trunk). Let
me know if you can see that I'm overlooking a better approach.

Cheers,

drew wills

Andrew Wills

unread,
Nov 19, 2008, 4:44:40 PM11/19/08
to cernunnos-discussion
---------- Forwarded message ----------
From: Andy Gherna <argh...@gmail.com>
Date: Wed, Nov 19, 2008 at 1:40 PM

> how do you make a phrase take reagents? Do they have to come
> from a task that surrounds it? can you give an example? this has
> me somewhat confused as some phrases say they take reagents
> but i'm not really sure how to specify them. For example, the
> ${checksum} phrase takes a @CONTEXT reagent. I think I
> understand how it works for ${valueOf()} since @NODE is specified
> by the current node in a node-iterator or an Attributes.NODE for a
> document defined like:
> <with-attribute key="Attributes.NODE" value="${doc(mydoc.xml)}">
> <echo-ln>current node = ${valueOf(.)}</echo-ln>
> </with-attribute>

I agree, absolutely, there are some surprising aspects about how
reagents work in phrases. Here are a few notes:
- most phrases take a reagent from the XPath expression
'descendant-or-self::text()'; in CRN XML, this is whatever appears
between the parentheses. E.g. in '${checksum(my-file.txt)}', the
value of 'descendant-or-self::text()' is 'my-file.txt'
- Some phrases take additional reagents, e.g. CONTEXT, NODE, CACHE,
CACHE_MODEL; there are only 2 ways to provide values for these, and
neither of them is particularly obvious.
- the first, and most common, is by using request attributes. Most
additional reagents for phrases listed in the main.grammar file take a
request attribute value as default. This is what you see happening
with <node-iterator>: as it loops nodes, it sets the value of
Attributes.NODE to the current node in the loop for tasks and phrases
(e.g. ${valueOf}) that occur within it.
- the other way is when you're defining the grammar entry in the
first place: use the CONTENT_MODEL reagent on <entry> to specify
additional reagents as XML attributes, just like tasks.

> This is a place where I'd like to help in getting the manual
> to explain this better. If all the "implied" or "globally named"
> or "common" attributes were defined and then peppered
> into the the tasks/phrases documentation, this would be
> very helpful.

Please do help in this way wherever you see an opportunity.

> Oh, and if you're taking votes, +1 from me for resource
> helper. this bit me when i was writing that deployment
> webapp.

awesome.

drew wills

Reply all
Reply to author
Forward
0 new messages