Why building Spindle is hard [sidebar]

Geoff Longman <[email protected]> Mon, 13 Feb 2006 21:32:10 -0500
Newsgroups gmane.comp.ide.eclipse.spindle.devel
Message-ID <[email protected]>
In this message I ramble about a bit, talking about how Spindle would
implement recursive folder searches into order to conform to the
Tapestry "lookup" rules. Also, I stumble upon the fact that there
really are no namespace boundaries (that's the FIRST PROBLEM) at all
in a multiple-app-per-war scenario. Lastly I propose some core api
changes that clean up some stuff and make the recursive searches
doable.

At the end of the last message (Pt.4) I was talking about the same
component having multiple names and that Spindle should find all of
these names and install the component "multiple times" into the
namespace.

What I didn't talk about was the mechanism Spindle would use to locate
a component,
possibly multiple times, with multiple legals names and determine that
this was the case.

I'm going to write it down now, more as a note to self or addendum to
the last message. This isn't a formal part of the series so it might
ramble and stray off topic.

in Pt.4, I wrote pcode in the last message that took
ICoreResourceLocations for all of the legal 'lookup' places and then
collected up all of the .jwc files found at those locations. Simple
when names are simple identifiers.

The approach is different when names have path parts. Now each lookup
location is the root of a much larger search. The folder of the
location and all of the sub folders of that location (to infinity and
beyond baby!).

Overlap is now unavoidable.

If I search /WEB-INF and then /WEB-INF/servlet-name we can see that
every location under
/WEB-INF/servlet-name is also a location under /WEB-INF. Thus the
multiple naming problem (and namespace boundary problems if there are
multiple application namespaces in the same war).

It's even worse. say we have two applications:

/WEB-INF/app1/app1.application
/WEB-INF/app2/app2.application
/WEB-INF/MyComponent.jwc

MyComponent is automatically available by the name 'MyComponent' in
both applications because of the 'webinf' lookup rule.

Further, the webinf rule makes every component in either application
available in the other
application - there are no namespace boundaries at all.

To illustrate, if we add this to the war file...

/WEB-INF/app1/Comp1.jwc
/WEB-INF/app2/Comp2.jwc

Then app1 can reference Comp1 as Comp1 and app1/Comp1, further it can
also access app2's Comp2 (app2/Comp2).

Similarly app2 can reference Comp2 as Comp2 and app2/Comp2, further it
can also access app1's Comp1 (app1/Comp1).

So, across two application namespaces there are 4 (FOUR!) distinct and
legal ways to reference the same two components. Two of which cross
namespace boundaries.

who boy, the deeper I try to figure things out the worse it gets. And
I have not yet gotten to
the THIRD THING.

Anyways, I'll try to ignore the above and describe the folder
searching as this entails a core
interface/implementation change I'm sure at least Hugo needs to hear about.

To search a single folder, ICoreResource already has a method called
lookup(). There is a method on ICoreResource called getSearch() but as
I will lay out, I think the existing search mechanism is
inappropriate.

The more I look at ISearch, the more it is becoming clear that is not
needed in the core api.

ISearch is used for one thing and one thing only in Spindle today. And
that is to find the files (not resource locations - real files or
their jar entry equivalents) that are Tapestry artifacts at the start
of the build.

Those files are used as a baseline so that at the end of the build we
can compare the baseline to the components that were actually
parsed/validated. The baseline - the actual = the set of all Tapestry
artrifacts that would not be picked up by Tapestry at runtime.

ISearch needs to work with files and not ICoreResources for the
following reason...

ICoreResources are resolved in the Classpath Way. It's easily possible
for a resource to be
"hidden" in the classpath if the requested resource was found in an
earlier classpath entry.

If the a jar file is first in the classpath, and it contains a file
called "foo.properties"in the default package, then
Class#getResource("/foo.properties") will always return a URL to foo
in the this jar, even if there is another "/foo.properties" later in
the classpath. Tapestry Resource's depend on this behaviour and hence
so do ICoreResource implementations. In other words,
ICoreResource.lookup() may 'hide' files in the same way.

ISearch is different, we need the baseline of all Tapestry artifacts
regardless of the Classpath order semantics. Otherwise the baseline
would not include any "hidden" files and we would not be able to
determine their "missing" status and record an error.

Since ISearch does not follow classpath order semantics, I can't use
it to search for
component's under an ICoreResource in a way that jives with the
Tapestry runtime.

I'm proposing some changes that get things back on track.

1. add method to ICoreResource

Object getUnderlier()

One would call this method to get the IDE platform specific object
that underlies this resource.

In Eclipse it woud be either an IFile or a JarEntryFile. The core
would never cast the returned object but it requires the returned
object will have valid equals() and hashCode() implementations.

2. remove method from ICoreResource

ISearch getSearch()

As the semantics for ISearch don't jive with the Tapestry runtime, and
the core never calls
getSearch() on an individual ICoreResource, this api is redundant and
IMO removing it from
ICoreResource will make the IDE implementor's life a little bit easier.

3. Change the ICoreResource method

void find(IResourceAcceptor) to void find(IResourceAcceptor acceptor,
boolean includeChildren)

Now we can duplicate the Tapestry lookup 'rules' in () in a Tapestry
runtime compliant way (by searching in the subfolders).

So what about ISearch?

I think the *idea* of ISearch still has the use it was designed for.
That is, to build that baseline view of the project.

in pcode this is what would be the build.

List<Object> allTapestryArtifacts = infrastructure.findAllTapestrySourceFiles();

foreach (ICoreResource resource - every resource that corresponds to a
*valid* tapestry artifact location) {

   parse, validate, record all errors for the resource.
   allTapestryArtifacts.remove(resource.getUnderlier());
}

foreach (Object left in allTapestryArtifacts) {
   record a "Tapestry would not see this file at runtime" error
}


So where is the ISearch? It would be needed in the implementation of
AbstractBuildInfrastructure@findAllTapestrySourceFiles().

But the implementation of findAllTapestrySourceFiles() is IDE platform
specific. Does ISearch need to be core api if the core never needs to
use it? In fact no, I realize now that ISearch need not be core api.

So, the choice is up to the IDE implementors out there... is it useful
to keep ISearch around or would you rather have a bit cleaner core api
and handle these searches your own way?

Geoff


-------------------------------------------------------
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