Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Tuesday, November 11, 2003

Proposal for HiveMind Project

(0) Rationale

HiveMind is a simple framework for creating pluggable, configurable, reusable services.

Simple: HiveMind is a way to create a network of services in terms of Java interfaces and classes; it cherry picks the most useful ideas from Service Oriented Architectures such as J2EE, JMX and SOAP, but removes the aspects that are typically overkill for most applications, such as service remoteability and language neutrality. HiveMind creates a natural network of related services and configuration data, all operating within a single JVM.

Pluggable: HiveMind enforces a complete separation of service definition and implementation. This is manifested by a division of services into an interface definition and a service implementation as well as a split between defining a service (as part of a HiveMind module) and providing the implementation of that service (potentially, in a different module).

Configurable: HiveMind integrates a service oriented architecture to a sophisticated configuration architecture; the configuration architecture is adapted from the Eclipse plug-in model, wherein modules may define configuration extension points and multiple modules may provide contributions to those extension points.

Reusable: HiveMind is a framework and container, but not an application. The HiveMind framework and the services it provides may be easily combined with application-specific services and configurations for use in disparate applications.

The API for HiveMind allows thread-safe, easy access to services and configurations with a minimal amount of code. The value-add for HiveMind is not just runtime flexibility: it is overall developer productivity. HiveMind systems will entail less code; key functionality that is frequently an after-thought, such as parsing of XML configuration files, logging of method invocations, and lazy creation of services, is handled by the HiveMind framework in a consistent, robust, and well-documented manner.

HiveMind fits into an area that partially overlaps the Apache Avalon project, with significant differences. HiveMind's concept of a distributed configuration is unique among the available service microkernels (Avalon, Keel, Spring, Picocontainer, etc.). Avalon is firmly rooted in a type-1 inversion of control pattern (whereby services must explicitly, in code, resolve dependencies between each other using a lookup pattern similar to JNDI). HiveMind uses a mix of type-2 and type-3 IoC, whereby the framework (acting as container) creates connections between services by setting properties of the services (type-2) or making use of particular constructors for the services (type-3).

HiveMind represents a generous donation of code to the ASF by WebCT (http://www.webct.com). HiveMind originated from internal requirements for a flexible, loosely-coupled configuration management and services framework for WebCT's industry-leading flagship enterprise e-learning product, Vista. Several individuals in WebCT's research and development team in addition to Mr. Howard Lewis Ship contributed to the requirements and concepts behind HiveMind's current set of functionality including Martin Bayly, Diane Bennett, Bill Bilic, Michael Kerr, Prashant Nayak, Bill Richard and Ajay Sharda. HiveMind is already in use as a significant part of Vista.

(1) Scope of the package

The package shall entail a core framework JAR (containing essential classes and services), a standard library JAR (containing generically useful services), along with ancillary artifacts such as Maven plug-ins and, of course, documentation, all distributed under the Apache Software License.

(1.1) Interaction with other packages

HiveMind has dependencies on several standard commons packages, including: commons-lang, commons-beanutils, commons-collections and commons-logging.

HiveMind makes use of the Javassist bytecode generation library, which is available under the MPL (Mozilla public license).

(2) Identify the initial source for the package

The initial code base has been developed by Howard M. Lewis Ship within the Jakarta Commons incubator.

http://jakarta.apache.org/commons/sandbox/hivemind

(2.1) Identify the base name for the package

org.apache.hivemind

Note: the current code base reflects an alternate package name, org.apache.commons.hivemind. Subsequent research has shown that HiveMind is not a suitable candidate for the Jakarta Commons. The existing code base will be migrated to the new package during the transition out of the sandbox.

(2.2) Identify the coding conventions for this package

The code follows a modified version of Sun's standard coding conventions, with the following stylistic changes:

  • instance variables are prefixed with an underscore
  • a newline is inserted before all braces

(3) Identify any Jakarta resources to be created

(3.1) mailing lists

hivemind-user@jakarta.apache.org -- User discussions
hivemind-dev@jakarta.apache.org -- Developer discussions and CVS update notifications

(3.2) CVS repositories

The package will use a root branch of the hivemind CVS repository (to be created).

(3.3) Bugzilla

The package should be listed as top level component, "HiveMind".

(4) Identify the initial set of committers to be listed in the Status File.

Howard M. Lewis Ship <hlship@apache.org>
Prashant Nayak <prashant.nayak@webct.com>
Martin Bayly <martin.bayly@webct.com>
Christian Essl <christianessl@yahoo.de>
Harish Krishnaswamy <hkrishnaswamy@comcast.net>
Knut Wannheden <knut.wannheden@paranor.ch>

This list represents the most active HiveMind participants within WebCT and on the Jakarta Commons Developer mailing list. Notably, Mr.s Essl, Krishnaswamy and Wannheden, among others, have already been actively mentoring other interested users on the mailing list in how to use HiveMind as well as contributing design ideas and patches to the framework itself.

Get this in your toolbox

I mostly try to stick to open-source other otherwise free tools ... I'm just cheap perhaps. Or I want to be able to look under the covers. But every once and a whilie, there's a reasonably priced tool so useful you have to get it.

For about $20, Blazing Tools Software's Instance Source is completly worth it.

It's a plugin for Internet Explorer that provides a real view-source window that is so chock-full of features I already can't live without it. Here's a few:

  • Colorized source as sent from the server
  • Colorized source as currently in browser (i.e., accounting for document.write(), etc.)
  • Frame aware -- different frames / framesets in different tabs
  • Element under cursor -- point the cursor at some text and see the HTML for it (yow!)
  • Selected text -- select some text, see HTML for selection

Basically, this is what I've always needed in my browser to help me debug HTML ... no more having to save HTML to the desktop to disect them, no more guessing what the HTML really looks like, no more frame pain. This is just too cool.

Monday, November 10, 2003

HiveMind Proposal on Tuesday

The proposal for HiveMind should be public tomorrow, Tuesday Nov 11th 2003. It'll be coming out of WebCT, from good buddy Prashant. This is a relief; I was starting to think in terms of total rewrite ... and even that wouldn't necessarily eliminate the intellectual property issues. This will. It's highly likely that HiveMind will find a home somewhere in Jakarta; the worst possible case is that WebCT will sign the grant and HiveMind will end up as a Tapestry sub-project. I'd actually prefer HiveMind to end up on its own, seperate from Tapestry.

Friday, November 07, 2003

Another great Tapestry quote

This one is from Jason Cox and showed up in the Tapestry user mailing list with the title Nothing short of beautiful :
At 11am this morning, the first version of our Web Portal prototype was completed. I'm sad to say that this is an Intranet project and so won't be usable for marketing Tapestry and for confidentiality reasons I cannot even name the company at this time. However, it is a very large company with an international presence, and the portal I am developing is going to be used mostly by managers and up to senior level executives.

I just want to give a big thanks to the members of this list, which I have been reading constantly.

On overview, though I doubt they will be examples of Tapestry best practices, the templates, specifications, and java code has all been very elegant. Not because I am such a stellar developer, but because Tapestry lends itself so well to MVC design. Once I got it in my head to fully implement all aspects of the portal in Tapestry, it took me about 3 days. There was far less effort than I've had using Struts or the custom framework we had at my last job.

The only low point is that I am not convinced that I particularly like how Tapestry handles Javascript. It just slams it all into the page. No cached references to .js files like you typically have in standard HTML and even JSP's.

On the other hand, I was able to perform some neat little tricks that would have been slightly more cumbersome using traditonal JSP development, including dynamic comments that only appear when certain conditions occur. Realistically, they're just there for reference if someone messes with the HTML template, but I didn't want them to actually appear on the page, so I nested them inside the error blocks.

Implementing my own Border web component was incredibly elegant, and if I would just take the time to properly 'HTMLize' it I can actually hand it off to a web designer in the future. Though I am still looking at dynamic menu generation and some other features that will make it less static. Not a high priority right now. What's the most amazing is that it actually works seamlessly with the ridiculously obtuse javascript files that most of our Intranet sites use to build their header and footer.

Now the next challenge is implementing a content management system that will also use Tapestry as its outward facing framework.

Tapestry momentum building

It's pretty clear that the momentum behind Tapestry is building; I just can't keep up with the flow of mail on the user list. Fortunately, the other heavy hitters (MindBridge, David, Erik and Harish, primarily) are keeping up. The first chapter in the book is being proofed right now; I suspect it will actually be available via MEAP next week-ish and the book will be in stores January or February; not so bad, considering the amount of last-minute rewrites. I suspect Tapestry will explode once that's available.

I know the pain, currently, of doing Struts development. I did some at WebCT, but I'm primarily doing UI work for NLG. It Struts, 1.1, Tiles, Tomcat, JBoss. Session facade layer to entity EJBs. Heavy use of XDoclet for the EJBs, for the Strut's Actions and servlet tags, etc.

However, any time I go to do anything, it feels like I'm having to juggle through so many different files. Some obvious things, such as having one Action forward to another Action (the second Action exists to get data from the facade and push into request attributes for use by the JSP) were just painful; I ended up creating a global forward for that since I had several different Actions that had to reference the data-loading Action.

Then there's the problem of figuring out how to reference actions from other actions, or from JSPs. Leading slash or no leading slash? ".do" suffix or no suffix? Global forward or local forward? Seems like this should be easy to do, but I had to do lots of fiddling around.

I don't like Dynaforms but the team standardized on them. You are very limited in terms of what data can be stored in a dynamform. Seems like it should automatically be able to convert to any types that takes a single string constructor parameter, but no ... very much hard coded (I peeked at the code).

Feedback: One of the four "pillars" of Tapestry (with Simplicity, Efficiency and Consistency). Boy do I miss Tapestry exception reporting! When things go wrong, I have no clue ... I just always have to run JBoss with the debugger and always have to set lots of break points. I ended up adding a custom JSP tag to our pages to display servlet request parameters and attributes and session attributes (a limited form of what you see in the Workbench) and that helps, a little.

Erik Hatcher couldn't believe I'd even consider a job developing Struts ... but, hey, I'm building up a war chest here while I start lining up the serious Tapestry opportunities.

HiveMind in transition

HiveMind is causing a bit of commotion in ASF-land ... the whole issue of who owns the code is causing grief. In the short term, the Jakarta PMC has taken down the HiveMind CVS repository and the HiveMind site is probably next. Can't blame them (and I is them ... I'm a Jakarta PMC member myself); the code shouldn't be there, in light of there being any ambiguity about code ownership.

So, HiveMind will be on a short hiatus until such time as WebCT grants the code and submits the proposal.

The great thing about being an independent consultant is that these issues go away; especially if I'm contracted to teach Tapestry courses, the moment I'm off the clock all ambiguity about IP ownership goes away. In the future, I'll make sure that any work I do has special exceptions for spinning off code into open-source land.

Saturday, November 01, 2003

HiveMind proposal almost there

A proposal to promote HiveMind up to a top-level Jakarta project is just about ready. A draft of the proposal has been bouncing back and forth between me and WebCT.

I did some reading and thinking on the plane to and from Virginia; when I get a chance, I have some good ideas about how to integrate HiveMind and JMX, to support things like:

  • Representing the MBeanServer instance as a HiveMind service
  • Representing standard MBeans as HiveMind services (using the MBean interface as the service interface)
  • Creating a performance monitoring interceptor that records invocation counts and times in an MBean
We'll see what other ideas come forth from this.

I've also been thinking of extending some of the event notifications inside HiveMind (thread cleanup and registry shutdown, specifically) to provide "levels" used to provide a coarse level of invocation order. This would be an intermediate step towards real dependency tracking. For instance, during a registry shutdown, I'd like the following order to apply:

  • Disable all configuration item list proxies and discard configuration items
  • Disable all service proxies
  • Cleanup all services
Since objects are registered in the ShutdownCoordinatory using just the RegistryShutdownListener interface, there's no way to order the notifications without additional information. I see the interface for ShutdownCoordinator changing to: public void addRegistryShutdownListener(int level, RegistryShutdownListener l);

Something similar would be good for thread cleanup; I'd like to temporarily disable the threaded and pooled service proxies, invoke the cleanup method on the service implementations, then re-enable the proxies.

Shameless self-promotion

Now that I'm in the world of independent consulting, I can't pass up any chance for good, shameless self promotion, such as this recent posting to the Tapestry user mailing list:
First, apologies are in order to the tapestry-user list community.... we kidnapped Howard Lewis-Ship for a couple of days for onsite training taking him offline from answering questions on the list - please forgive us :) We, in the Darden Solutions Group within Darden Graduate Business School at the University of Virginia, decided to switch from Struts to Tapestry a few months ago for our next project. Since then we've been an early previewer of Howard's wonderful Tapestry in Action book, and working our way through our first passes at implementing a Tapestry application. We had built enough to know what we didn't know and where we wanted a quick boost of insight.

Our mission is to develop a suite of applications in a short period of time. We brought Howard in for two days of onsite training to give us a kick start in the right direction and ask him detailed questions about implementation issues we've encountered. It started as a semi-formal presentation to introduce us to Tapestry and quickly took a custom tangent into the areas we were most interested in, like creating custom components, how parameters / properties / bindings work, how do to deal with form validation, and much more. Howard was adept at fielding our random questions. At no time did we stump him. He even very quickly picked up on our business use cases and even offered suggestions in that realm, unrelated to the gory Tapestry details we brought him in for.

Our team greatly appreciated having Howard's time and expertise. We have easily saved weeks of time learning this stuff the hard way, and given everyone a very quick boost into how Tapestry works and how to use it effectively and properly.

Everyone here knows that Tapestry can have a steeper learning curve than other less powerful frameworks. If your project has the same delivery timeframe as ours ("yesterday"), yet you want to do it right, I suggest you consider doing what we did and bring Mr. Lewis-Ship in for training.

Erik

That's Erik Hatcher, "the Ant guy", frequent speaker at No Fluff Just Stuff symposiums and all around great, well-connected, finger-on-the-pulse kind of guy.

I had a terrific time down in Virginia, it's a real rush to spend two whole days thinking on your feet. Everyone on the team was very complimentary about the presentation ... which just goes to show you how different things look from opposite sides of the podium. I was concerned that I was mumbling incoherently like an escaped mental patient, but that's not how I came off :-). It was very rewarding to see the connections get made inside people's heads. Of course, the first major questions hit all of Tapestry's dirty laundry: the awkwardness of component parameter directions, for instance. But part of open-source is that your dirty laundry it out for all to see, and that no project is ever finished.

We had a really fun time, with discussions of Tapestry and other technologies rolling out of the class room and out over lunch and dinner. It was a quick trip, but we made it out around Charlottesville thursday night, which has a very European-styled "mall" ... an open, wide, cobblestone walkway that extends several blocks and is filled with book stores, coffee bars and the like. Refreshingly, it doesn't seem gap-ified (didn't see a Starbucks or any other national chain). We also swung by the Gravity Lounge, a cool mix of Internet cafe, book store, and live performance stage. It also has an Art-O-Mat, from which I purchased some art for my wife--a wooden block with a kind of beat-style/kitch black cat. A good draw from the Art-O-Mat, since you never know what you'll get ... my wife just loves cats!

So far, that's two Tapestry consulting gigs this year ... one was way back in January for a company out in Los Angeles. Not so bad since I haven't been seeking these out, and have had to work around full-time jobs to accept the gigs. I haven't really started marketing myself since I pretty much have a full-time gig going for the next few months at NLG, but as that looks to wind down (probably late January) I can start trying to schedule out engagements for 2004.

Friday, October 24, 2003

Composition Trumps Inheritance

Just a random thought ... composition is more powerful than inheritance because, using composition, you always work in terms of a single, outward facing, public interface.

With inheritance, you have the public interface and the "protected" interface: the constructors and protected members inherited from a super-class. This creates a greater "surface area" that you have to adapt to.

With composition, you can extend an existing service (yes, this is about HiveMind because Tapestry is so inheritance based) by wrapping around it. You don't subclass to the other service, but you do delegate to it.

In some ways, inheritance is just a awkward and limited kind of composition. If class A inherits from class B, then class A implements the same interface as class B and delegates all methods to class B that are not overridden by class A. super is just explicitly delegating to B, and the rest is syntatic sugar for automatically delegating to B. Well, pretty much (this concept breaks down once you enter the idea of a super class invoking methods overriden by a subclass, not to mention reflection).

I think UML has recognized this for a long time; I've always had a hard time creating sequence diagrams for classes that make use of inheritance (and recursion) because there isn't a really good way of representing those kinds of invocations. With no inhertiance buts lots of delegation, such sequence diagrams are completely reasonable.

Working for the competition?

Just noticed a new open-source MVC web framework, Jucas. Interestingly, its built on top of ... HiveMind.

Last day at WebCT

I'm finishing up my last day at WebCT. I start, as a sub-contractor, at NLG on monday(!). No rest for the weary. It's awkward and a bit sad to be moving on, but I'm looking forward to a few months out, when (hopefully) I largely support myself with Tapestry and HiveMind. I've spent a lot of time in my career "batting cleanup", coming in late to projects and products to bring order out of chaos. I was refactoring (in PL/1) before I even knew such a term existed (our group coined the word "Howardizing") ... not to mention creating unit tests and all that ... everything old is new again.

I'm also looking forward to concentrating on fewer things; I've felt very distracted at WebCT ever since I got back from sabbatical, because I came back too early, and have had the book (and shopping for a house, and moving, and now the job switch) eating at me.

Definition of Service Oriented Architecture?

Looking around online, most of the definitions of SOA that I find are inextricably tied to language neutrality and remoteability. Under those guidelines, SOA is restricted to solutions like SOAP.

I find that too restricting. The goals of SOA (as I define it) are to enhance reuse and manage complexity. A key way to achieve both of these is to remove statefulness. When objects don't have individual state, they are easier to combine together ... because state implies that there is an order of operations that gets in the way of easily combining services together to form more powerful services.

In HiveMind, services may have state ... but there's a cost on any application that makes use of internal state.

Remoteability is another holy grail that costs more than it gives back. EJB's local object support is an admission that remoteability of services is the exception, rather than the rule, and that a container is useful even if objects are shared within a single JVM.

When a service needs to be remote ... go for it! XML over HTTP or even full-blown SOAP is appropriate. But once the gears start turning inside your application, keep it all in the same JVM. A common maxim I've heard and used is "XML at the edges"--meaning that XML is an inefficient format (especially in the flexible, dynamic Java environment) to represent data in within your application, but is a good choice when presenting an external face to your application. I'd extend that to "Remote access at the edges" as well.

Thursday, October 23, 2003

Services vs. Configurations

One stumbling block with HiveMind which keeps coming up in various conversations is a pattern like this:
  • I want to define my foos in a HiveMind configuration
  • Some of the foos will be defined at runtime (from the database)
  • Some attributes of the foos will be settable by users at runtime
  • How do I update my HiveMind decriptors with these changes?

You don't. HiveMind is used to describe the static configuration of your application. This is equivalent to saying "How do I change the default value of a final static field in one of my classes (and store it back into a JAR (and distribute the change around the cluster))."

What your HiveMind configuration will store is the default configuration of foos until a) they are updated by a user and/or b) new foos are discovered at runtime.

What you need is a service that can answer the question: "what is the configuration of my foos?" This service will have access to the configuration ... but also to the database for updates and overrides. It will integrate that information drawn from the HiveMind configuration with overrides from the database and provide a consistent view. If you are smart, you'll break it apart into a master service with several additional services (for the discovery piece, for the database override piece).

I can't think of a scenario where external code (code that is not a HiveMind service) should directly access a configuration extension point. It is allowed, the API is there ... but in all reasonable cases, putting a service between client code and the configuration data is the best bet ... especially for applications that expect to grow in the direction of greater dynamicism.

Wednesday, October 22, 2003

HiveMind Presentation report

So, last night was the HiveMind presentation. It went rather well on the whole; people seemed interested and stayed for the whole (long) Q&A. I'd forgotten how exhausting doing a live talk can be ... plus it was hot in the room.

Major stumbling blocks:

  • The benefits of a service-oriented architecture are intuitively obvious to me but I had trouble explaining them off the cuff. SOAs promote testability and reuse, by allowing easy decomposition of complex problems into simpler building blocks. SOAs make you think about state ... where you need it, where you don't.
  • XML and XML rules. This prompted a distracting side track about the usefulness of XML and Dan Jacobs was really questioning the concept of having a stack-oriented scripting language (regardless of how focused it is) as part of HiveMind. People were also looking for alternates to XML.
  • There were some concepts that needed to be fleshed out a bit more; especially the idea of using a factory to construct new services (which came up again in Q&A).
  • A few key ideas that were out of scope for the presentation (such as the threaded service model) should have not been mentioned at all.
  • Needed to set the expectations better ... people kept coming up with ideas (remote services!) that are, basically, just J2EE in disguise.

Still, the Q&A was lively and people seemed interested. It revved me up for my upcoming ApacheCon appearance (which I need to practice for!). Life is just too busy.

Friday, October 10, 2003

HiveMind presentation at WebTech Users Group

I'll be giving a presentation about HiveMind at the Boston-area WebTech user's group on Tuesday, Oct. 21st. Meeting starts a 7pm and will be a bit over an hour, plus time for Q&A.

The most common question at these events is: "Is this presentation online?". The answer is: yes (follow this link).

Monday, October 06, 2003

Thinking about coding

Do you think about coding? A lot? I do.

My own personal style has shifted a bit recently; I've adopted a few minor tricks, like naming instance variables with a leading underscore and I've stopped naming interfaces with a leading 'I'.

More than that, I've really been trying to adopt an every more spare style in my interfaces. I'm looking for the Perfection and Simplicity that Ken Arnold talks about. I often visualize my object interfaces in terms of a suit of armor, which should have the minimal number of openings. Ken talks about the surface area of an interface, that can expand when there's a lot of inheritance involved ... or my own personal issue: convienience methods.

When the Collections framework first came out, I was aghast that useful things (like sorting) were in a static class. Ugh! Surely that's no OO!

I've since come to understand the usefulness of that. My early OO was in Objective-C. Between categories (an early forerunner of aspects) and a lack of garbage collection, you could end up twisting yourself up. Better to put a method onto an existing class than to have to create a whole new class!

But nowadays, in terms of keeping my surface area small, I seek out places where I can replace convienience methods (often protected methods in some base class) with a static utility method.

I look at methods now (I'm doing this somewhat in Tapestry) and say ... hey, change a few variable references to accessor methods and you could pull this entire method right out of this class. For example:

public class AbstractLineItem  implements LineItem
{
   private int _quantity;
   private double _cost;
   
   protected double computeTotal()
  {
    return _quantity * _cost;
   }
}
could easily be replaced by:
public class LineItemUtils
{
  public static double computeTotal(LineItem item)
  {
    return item.getQuantity() * item.getCost();
  }
}

Note the big advantage: its a public method expressed in terms of the interface not the class. Suddenly, the logic here is so much simpler and more testable. In HiveMind terms, it may not be a static class, but instead of service used to calcuate the total ... same difference.

This does an end run around problems such as multiple-inheritance. Too often, I want MI because there are convienience methods in base class A and base class B that I want access to. Moving those convienience methods out into a static class or service removes the need. Suddenly, those base classes aren't so useful either; I'm more likely to start with java.lang.Object and just implement the right interfaces.

Nowadays, I look at the Tapestry code base and think ... woops; a bit too much of Tapestry is inheritance-oriented rather than interface-oriented. A page or component in Tapestry fills different concerns (model, view and controller) at different times and perhaps the mythical Tapestry 4.0 will address that (Tapestry 4.0 is my personal code word for the total , non-backwards compatible rewrite of Tapestry that I would only think about under the guise of a JSR, so don't hold your breath). I'd love to seperate out the IComponent interface into seperate objects and interfaces to represent property storage, component hierarchy, rendering and request handling.

In the meantime, I'm finishing up at WebCT. HiveMind is going gangbusters (just added a pooled service model). I spent the weekend doing major revisions on chapter 1 of The Book and feel I've nailed it. ApacheCon is coming up, as is my new and changed life as a happy-go-lucky independent consultant. Now if I could just get the Vista application deploying into WebLogic 8.1 properly, I'd be a fully happy camper.

Friday, October 03, 2003

ThreadLocal and thread pooling

Anthony Eden's post, ThreadLocal and thread pooling, illustrates one of my HiveMind goals. His post was about the importance of clearing out ThreadLocal objects at the end of a (servlet) request.

HiveMind includes a service for storing thread local information in a generic fashion ... effectively, a ThreadLocal Map of data. It all hooks into the common ThreadEventNotifier service, so that the thread local data can be discarded at the end of the request in a simple, uniform manner.

I constantly say that a good framework should make the easiest approach the correct approach. This is another example ... in this scenario, the correct approach is to discard thread local data at the end of the request and it is easier to use the ThreadLocalStorage service than to roll your own ThreadLocal object ... and certainly easier to let HiveMind handle the end-of-request cleanup than to do so in your own code.

Thursday, October 02, 2003

Tapestry in Action

Manning has started to market my book on Tapestry, "Tapestry in Action". This is great stuff.

A glorious new world of opportunity!

Today. Is. The. Day.

After months and months of thinking about it, even agonizing about it, today is the day that I resigned from WebCT.

This is good; this means I will be available for Tapestry (and HiveMind) consulting! That's the whole point, today is the day I launch a consulting career.

Even better, in this questionable consulting market, I have pretty much full time work as a sub-contractor through my old boss, Tim Dion (now CTO of Riverton). How could anyone turn this down ... more freedom, a chance to pursue the dream (full time Tapestry!) and, at the same time, steady work. Still remains to be seen exactly how I'll balance Tapestry consulting with my responsibilites to Riverton's clients' ... but I'm sure we'll work something rational out.

Shortly (once all these details are worked out) I can start actually drumming up business.

Wednesday, October 01, 2003

Adding some lifecycle support to HiveMind

Just checked in some changes to support some basic lifecycle on a HiveMind registry and its services and configurations.

There is now a Registry.shutdown() method that shuts down the registry. All proxies will stop working (service methods throw ApplicationRuntimeException), same with all configurations. Core service implementations can now implement RegistryShutdownListener to be alerted when the registry shuts down.

I like the idea that proxies stop working when the registry shuts down so much that I changed the deferred service to always return the proxy. That, with C. Essl's inner-proxy solution (to avoid synchronization once the service is created) is very cool. No guesswork, no dangling anything ... shutdown the Registry and kill all the services (at least, all the non-singleton services).

It's all good baby ... going to add more involved lifecycle for threaded services (so they can tell when they are discarded) and, perhaps, add a pooled service model (with notifications when the service is activated and passivated).

More super-secret news coming soon!

Friday, September 19, 2003

Excellent discourse on WebObjects and Tapestry

Drew Davidson, (co-?)creator of OGNL, recently posted this message about WebObjects to the Tapestry user mailing list:
I went to WWDC 2001 full of excitement about WO, since at that time they were just getting the pure Java 5.0 version out the door. I had talked with them previously about how they were going to transition from ObjC to Java and was concerned about the API.

Their main focus was to make it API compatible with ObjC - something I thought was ridiculous. From their point of view, I guess that their customers were clamoring for this because it would make transitioning their applications from ObjC to Java easier; most of the WO work we did at Running Start (where I worked with Erik Hatcher) during 2000 involved using the ObjC-Java bridge and the Java API to WO that was provided. So there was an existing Java API for WO.

However, this decision had long-term consequences to users. The API as presented by the ObjC-Java bridge was an Objective-C API - no two ways about it. It was not Java-like in the least. The API used internal collections (NSArray, NSDictionary, etc.) instead of the soon-to-be-standard Java Collections in java.util. The method names were ObjC-like and not JavaBeans patterns where that made sense. ObjC concepts like categories and inherited class methods that Java does not support were rough translations and sometimes the disconnect was obvious.

Short term this is a benefit as developers can more easily port their code.

Long term, however, this means that you must live in the Apple WO world instead of the bigger Java world. Adaptors must be written when interaction is required between APIs and standard tools for doing JavaBeans stuff just won't fit without making custom BeanInfo objects for the necessary objects.

The WO API itself was written very long ago (it started back at NeXT back in 1994, for crying out loud!) and it was written with the perspective that you build a WO *application* that acts as an application server. It does not take into account the notion of Application Servers and therefore solves the entire Application Server role from soup to nuts. At the time it was written this was sensible.

But the world has moved on. Servlets are now the standard way to deploy Java web applications. Apple has made efforts to integrate WO with the Servlets, and even integrate with JSP, but IMNSHO they have not really made it feasible to develop and deploy this way. The API is just too elderly and they needed to have ripped a new page off the notebook, taken the concepts and started over. The concepts are still very valid. Nothing else (up until Tapestry) had attempted to really componentize the web, and that is very much to NeXT's (then Apple's) credit. When Howard started over with the basic assumptions of Application Server, Servlets, etc. and applied the excellent concepts from WO he created Tapestry. Tapestry is, to my mind, what WO 5.0 should have been. The failure was due to Apple's doing what the customer *wanted* and not what they *needed*. I'm sure that Apple's rationale for doing what they did was to support their customer base and not lose those customers. But look at their market now - they are niche. But the 3 WO customers left probably had a very easy time porting their 4.5 applications to 5.0.

BTW I mentioned the collections problem at WWDC (in one of the sessions, in front of everyone) and was brushed off by Ernie Prabakhar (the manager in charge of WO at the time) by him claiming that their collections were not functionaly compatible (mainly due to null handling); I told him that I had integrated the two without conflict with some facades that wrapped the NS stuff to Java Collection stuff. It worked, but was wasteful because of all of the adaptors hanging around and the constant awareness you had to have at integration points. They really had no interest in solving this problem, and it was clear they just wanted me to shut up and go away because I was expressing concern and critique instead of praise and adulation.

You, Jamie, are correct in your assesment that WO has stopped evolving. It has become a niche solution wherein you have to buy into the entire Apple web development package, rather than being able to use the tools you want and exclude those you don't. The only company that can get away with this kind of behaviour and still keep market share is Microsoft.

- Drew

Wednesday, September 17, 2003

Components Vs. MDA? (vs. J2EE)

Been reading some of the blog by Rickard on Model Driven Architecture and the resulting maelstrom at TheServerSide.

I have a pretty deeply ingrained abhorance of code generation. Note bytecode enhancement at runtime; that I'm really into. There's no baggage with that, no chance for the build process to screw it up, no need for a developer to go modify the output, no chance for bad merges or lost changes.

I've never like code generation; my early exposure to NeXT and InterfaceBuilder really showed off why it wasn't necessary for GUIs and I still don't think its needed for enterprise apps.

Code generation is a last resort that should only be used when real techniques fail. For me, real techniques are combinations of components and service-oriented architectures .. the kind of stuff you see in Tapestry and HiveMind.

In MDA, you define your system in terms of objects and relationships ... but then you spew out code representing that system that isn't isomorphic to the system you defined. If it was truly isomophic, you would be able to reconsititute your original model from it, but that's simply not reasonable.

In my vision of a proper MDA, the objects and relationships in the model would be directly expressed by matching objects and relationships in the runtime image. In Tapestry terms ... application extensions and helper beans; in HiveMind terms, services and implementations. In both cases, I would expect that the MDA would provide a library of the components (and service implementations) referenced in the model and the runtime image.

Tapestry has room for meta properties in all the key elements; it is quite reasonable for a tool to plug in meta data into pages, components and helper beans to support seamless round-trip engineering ... reading the output to reconstitute the original model. Wouldn't be, couldn't be, perfect ... but it would be better than spewing out code.

Still, if the underlying problem is the complexity of J2EE in terms of getting reasonably sized projects working ... then J2EE is overused and should be bypassed. EJBs don't lend themselves to real, lightweight, agile development models (XP or not) ... they are too reliant on the container and on the runtime environment to be easily tested outside of that container. Any reasonable development model involves running frequent tests ... for example, Tapestry has around 400 tests that run in 40 seconds (and I want to speed that up significantly). HiveMind has 160 tests that run in three seconds. Did I break something? Click the test button.

At work, for me to test an EJB change requires a build (to package the EJB and deploy it) which is currenlty taking about ten minutes. You read that right ... we have some issues with our build environment (they're being worked worked on, but ... "House built on a bad foundation ...")

So, with every change requiring a minimum of ten minutes to validate (usually, much, much longer) agile is dead. Refactoring to use HiveMind (or other microkernels) could support a fast, robust, agile methodology ... and bring into question whether MDA makes sense.

Meanwhile, my other peeve with MDA is that it assumes there will be one uber-developer (the architect) who can create the model, and create the code generation scripts (or modify the supplied ones). How many people can wear that many hats? Doesn't that create a choke point? Isn't that another block to agility ... you don't fix your code, you have to find and fix the code that generates your code, regenerate all, retest, hope it didn't break something outside your fiefdom.

MDA. Offshort outsourcing. Mission statements. ISO 9000. What's the next fad that's going to distract me from getting actual work done?

Monday, September 15, 2003

Presenting at ApacheCon

I'll be presenting an all-too-short Tapestry session at ApacheCon on Nov 17th at 11am. I have 60 minutes to talk Tapestry and handle Q&A ... glad I already have some materials ready, but I don't know how much I can cover in that amount of time.

Friday, September 12, 2003

HiveMind call for participation

HiveMind is, I believe, ready to go! The last few days have been spent refining the framework in a number of small ways. Essl Christian has really gotten very much involved, even contributing a useful patch that addresses the complex class loader environment within Tomcat. He's also provided some great input that led to further shaving away of unwanted code. Sometimes coding is like sculpting ... you just remove the stuff you don't absolutely need.

I'm now interested in putting together a real community to bring HiveMind forward. This will mean putting together a real proposal to move HiveMind out of the sandbox and into Jakarta commons proper. A community around HiveMind would be a good start.

My vision for HiveMind going forward:

  • Select a starting contributors community. Me, Essl, Bill Lear?, Harish?, David Solis?
  • Move to Jakarta commons w/ a charter, voting and all the dressing. Yea! More administration!
  • Move the source code over from sandbox to proper. Reorganize it into a Maven multiproject
  • Progress to a 1.0 release
  • (In parallel) Begin to add additional sub-projects to wrap common code as resuable HiveMind modules. Examples: object pools, database pools, Lucene?, Betwixt?, Hibernate, etc.
  • Conquer the world, and all that

So, if interested, drop me an e-mail or post to the Jakarta Commons Developers list.

Sometimes testing numbers just don't make sense

Based on what I did yesterday, I thought I'd see if I could improve the efficiency of the LoggingInterceptor by directly invoking methods on the commons-logging wrapper class, rather the the Log interface. Fortunately, I decided to do some tests to see about relative costs. I don't fully understand the results (Windows XP, Sun JDK 1.4, Hotspot server) ... obviously, Hotspot is doing something interesting to the code.

Run Log4J Direct Jakarta (Interface) Jakarta (Impl)
Run #1 741 711 701
Run #2 1622 671 551
Run #3 661 521 541
Run #4 641 540 571
Run #5 621 541 551
Run #6 610 521 551
Run #7 611 531 560
Run #8 611 531 551
Run #9 621 521 550
Run #10 611 521 551
Run #11 611 530 551
Run #12 611 521 551
Run #13 611 520 561
Run #14 611 531 550
Run #15 611 511 531
Run #16 581 510 581
Run #17 621 531 601
Run #18 631 571 570
Run #19 621 541 571
Run #20 621 541 570

The tests invoked isDebugEnabled() 50,000,000 times. I did what I could to help ensure that things didn't get unrealistically inlined.

What I don't understand is why going through the Log interface is sometimes faster than either going through the Log4J Logger class, or through the commons-logging Log4JCategoryLog class. I checked the commons-logging code ... Log4JCategoryLog is a very thin wrapper that delegates to a Logger.

Anyway, until I find better tests for figuring out performance, there's no reason to change the LoggingInterceptor code.

Thursday, September 11, 2003

Improved HiveMind efficiency

I was thinking about interceptors in the shower this morning (best place to do serious thinking) and was also thiking about the performance results I got a little ways back. It seems like the big hit as the constant calling of interface methods instead of instance methods. It's well known that calling a method on an interface is much slower than calling a method on an object instance ... the JVM has to do a more expensive dynamic lookup and there are fewer options for optimizing the call.

One idea I had was to rework how interceptors work, so that all the interceptors would work together to make a single interceptor class. Didn't seem like the right approach, since it would be a lot of work, and I think it will be rare for a service to have more than two interceptors. Hard to test, hard to implement, no assurance of a return.

Next, I considered having each interceptor subclass from the previous; this would allow calls to use super.foo() rather than _inner.foo(), which would be more efficient (a super-class invocation rather than an interface invocation).

Then I realized that I always have the actual type and instance of each interceptor (or the core implementation) ... which means I can build interceptors in terms of concrete classes rather than interfaces.

To see if there was any benefit, I needed to test. I extended my test harness to add to new Adder services; the first is like the Singleton service, but has one NullInterceptor (this interceptor simply calls through to the next object). The second has two Interceptors. First run (JDK 1.4, Hotspot server) showed the cost of those interceptors:

Run Bean Interface SingletonDeferred ThreadedOne Interceptor Two Interceptors
Run #1 211 390 2183 2824 3185 2393 9174
Run #2 250 2343 2324 2864 3014 2434 9203
Run #3 240 2354 2323 2844 3054 2394 9253
Run #4 241 2333 2353 2824 3045 2403 9183
Run #5 231 2353 2333 2825 3064 2383 9194

Compare column "Singleton" to "One Interceptor" and "Two Interceptors". I don't exactly have a theory for why the difference between one and two interceptors is so large. Also, the value is red is not a mistake, we've seen it before ... that appears to be Hotspot making an early optimization when there is only a single implementation of the interface; later it must go back and rewrite things when new implementations show up and it can no longer be certain that interface test.Adder is always class test.impl.AdderImpl.

I then reworked the interceptor code to use the actual class to call the next inner-most (interceptor or implementation). This is possible because of the order of contstruction: the core implementation is constructed first, then the lowest order interceptor is wrapped around it, the the next lowest order interceptor, and so forth. This time there was a dramatic change:

Run Bean Interface SingletonDeferred ThreadedOne Interceptor Two Interceptors
Run #1 210 361 1993 2593 2925 2203 2233
Run #2 210 2153 2144 2613 2804 2194 2213
Run #3 220 2143 2153 2594 2784 2203 2203
Run #4 221 2143 2153 2583 2804 2194 2193
Run #5 220 2143 2153 2574 2814 2193 2213

And so we see the desired outcome; adding interceptors is now a small, incremental cost.

Why is this important? We often hear about the 80/20 rule, that 80% of the performance problems are in 20% of the code. My concern is that all the extra layers of interface invocations will become a visible factor, cumulatively ... but because all those calls are so completely, widely distributed, they will become impossible to track down. Sort of grit in the machinery ... no single, large point of failure, but a kind of overall friction in the works.

Given just how many iterations of my code loop I need to get measurable results (50,000,000), that's probably not a large concern ... still it's nice to claim I've optimized HiveMind for performance.

Tuesday, September 09, 2003

More substantial docs on HiveMind

I've added several new documentation items to the HiveMind site. One describes how to "bootstrap" registry ... exactly what code you execute to locate and parse all the HiveMind module deployment descriptors and form a Registry (it's just a couple of lines). A second page describes how to override a service with a new service. Finally, a big page is a case study of how HiveMind is used in Vista to manage startup and shutdown logic.

Meanwhile, HiveMind is really taking root at WebCT. Several new projects within Vista will leverage HiveMind in one way or another; all of these fit the HiveMind pattern: service oriented, with distributed configuration needs. For example, a new data access layer is in the works, and different tools and services will contribute DAOs (Data Access Objects) for different types of databases. Rob Lorring has just put together a JMX-based monitoring package using HiveMind ... different tools contribute different monitors. The new caching service is configured using HiveMind as well.

The recent call for contributors to form a HiveMind community is also coming along, with a few people popping up to see what it's all about. Once I have a free moment (a laughable concept) I'll put together a proposal to move HiveMind from the sandbox into Jakarta commons proper.

Friday, September 05, 2003

HiveMind --- now with downloads

I hacked together some stuff to allow downloads (binary and source) of HiveMind. It's out of my home page at jakarta.apache.org. There's some definite interest building in HiveMind ... pretty soon, it'll be time to form an actual community and propose moving it out of the commons sandbox.

Wednesday, September 03, 2003

"Tapestry in Action" cover

I got goosebumps! Manning has decided on the cover illustration and here's what the book will look like:

With luck, the first chapters will show up on MEAP in about two weeks.

"Tapestry in Action" will be MEAP

Got some confirmation that the book will be available as incremental, online PDFs prior to dead-tree format. Not sure at this time what the schedule is ... there's still a lot of copy editting, indexing and production to be done.

In other news ... I'm trying to set up the necessary definitions to allow Javassist to build inside Gump. I'm awaiting S. Chiba's permissions, even though I probably don't need it -- can't see why he'd object. This will be good for other Gump projects that have a Javassist dependency, such as Tapestry and HiveMind.

Saturday, August 30, 2003

HiveMind EJBProxy is back

Just got the latest Javassist JAR from Shigeru Chiba and he's fixed the problem related to try ... catch that was breaking the EJBProxyFactory service. He's on Tokyo time, so communications can take a while; I believe he wants to do a release of Javassist 2.6 in the next few days.

Pull the fork back out?

So now the "Tapestry in Action" goes into production; a complicated process of copy editting, typesetting and marketing. One part of this is that I must go back through the manuscript and create the index, a new and mind-numbing procedure. In addition, it's looking good for Manning to public the book under MEAP (early access / online versions of the chapters).

I got some odd feedback from Maryan Bace (publisher at Manning) and Ted Neward about a previous blog. Ted felt that:

The last set of reviews were pretty positive, except for Ryan Cox who was, like, 95% positive, but really didn't like a few aspects of the book ... didn't like the Hangman example app, didn't like the sidebars and warnings, didn't like the deep investigation of the Virtual Library application. This is part of the process ... about fifteen different people have reviewed some or all of the book, and they disagree wildly. Mind Bridge, Geoff Longman, Greg Burd and Dave Buck think the book is aces, and they are my real target audience so I'm not exactly worried.

... was a slam on Ryan. If it could be interpreted that way, I apologize profusely. Both Ryan Cox and Bill Lear were the most negative about the book, and therefore the most useful. Both took considerable amount of time to really work through the manuscript and provide reams of questions, notes and challenges. That's how the process works ... it's suprisingly democratic; as the author, I'm required to respond to each and every reviewer comment. Responding to Bill's comments is what, in my opinion, kicked the book up from a pile of facts into a real, flowing, useful tome. Ryan's comments came later, and so couldn't have the same impact, but he definately zeroed in on particular areas that needed expansion and elaboration. The point of my original blog was that even someone who disagreed on some fundamental aspects of the book was still very positive about the book in general.

Tuesday, August 26, 2003

Performance of different HiveMind service models

Thought I'd do a little comparison of how the different HiveMind service models perform (relative to each other). First of all, I found a huge difference between Hotspot client and Hotspot server; HiveMind seems to really benefit from Hotspot. Second, the big difference is between simple virtual method invocation and invoking interface methods ... an order of magnitude difference with HotSpot server. I think the big difference is that the very simple adder bean gets completely inlined by HotSpot. I'm going to try and find a way to convince HotSpot not to inline the bean code.

The method being invoked is:

public long add(long arg0, long arg1);

Each run invokes the method 50,000,000 times. The elapsed time is the time in milliseconds. Note that the Java clock (System.currentTimeMillis()) is very, very innaccurate.

JDK 1.4, Windows XP, Hotspot Server

Run Bean Interface Singleton Deferred Threaded
Run #1 200 320 2003 2544 2874
Run #2 220 2324 2223 2614 2844
Run #3 210 2454 2894 2654 2894
Run #4 210 2223 2083 2604 2734
Run #5 230 2083 2073 2594 2794

Bean - invoking the method directly on the service implementation class (that is, instantiate the class without using HiveMind)
Interface - Invoking the method against an interface, not object, reference
Singleton - Invoking the method against the service using the singleton model (as expected, very close performance to interface)
Deferred - Invoking the method against the service using the deferred model (slight increase in time reflects the extra proxy and synchronized block)
Threaded - Invoking the method against the service using the threaded model (another increase for the additional proxy and the ThreadLocal stuff)

JDK 1.4, Windows XP, Hotspot Client

Run Bean Interface Singleton Deferred Threaded
Run #1 381 2183 2193 5157 8573
Run #2 380 2203 2213 4116 7701
Run #3 371 2203 2243 4186 7681
Run #4 371 2203 2253 4126 7701
Run #5 371 2203 2213 4126 7701

Big difference switching from Hotspot server to Hotspot client; hard to say why in particular ... less inlining but also less of a difference between bean and interface, but the additional cost of the extra proxies (for deferred and threaded) and for the ThreadLocal stuff (for the threaded model).

Something useful to do next would be to create a more realistic bean, something less likely to be completely inlined by HotSpot, so we can really see the difference between the Bean and Interface invocation costs. I think the take away is that the big cost is the difference between Bean and Interface, the cost of adding Deferred or Threaded service model is costly, but incremental.

In addition, the little bit of performance testing I've done using the HiveMind test suite shows that the one-time cost of parsing the HiveMind module deployment descriptors is signifcant; all else is dwarfed by this cost. I'm concerned, or perhaps merely curious, about performance when you build a full application from a wide mix of singleton, deferred and threaded services. Services calling services calling services, all as interfaces, all with proxies and perhaps interceptors ... what will be the cost of all that wrapper code? On the other hand, what are the costs of executing a managed object as an EJB? I'm fairly confident that refactoring stateless session EJBs into HiveMind services (deferred or threaded) would be a net win performance-wise.

Monday, August 25, 2003

Stick a fork in it!

Tapestry in Action is complete, uploaded and ready to go. Well, not quite, tomorrow when I'm not quite so bleary eyed, I'm going to make a final pass over the manuscript to find any lingering errors. Still, this has been eight months of fairly continuous effort, overshadowing everything else going on in my life, stealing my summer, distracting me from extending Tapestry and HiveMind.

Next up, fill in the Tapestry documentation gaps and make sure Tapestry is 3.0 (final) before the book is on the shelves. Clock is ticking, we have about two months!

Hopefully, many hands will make light work; looks like we may have three (!) new Tapestry members shortly, including Erik Hatcher (aka, Mr. Ant), Tsvetelin Saykov (who works with Mind Bridge) and Harish Krishnaswamy.

Meanwhile, a few folks are not too happy that I responsed to some flame-batewith some industrial strength venom. Was it unprofessional? Perhaps; I do take Tapestry very, very personally (which is paradoxical with how pleased I am at how the community and developer pool is growing). Still, leaving a challenge like that unanswered is not in my nature ... and if you read what's in my reply, I think you'll see that, even within the flame, I'm still on-message and marketing! marketing! marketing!

And now to bed ... tomorrow I buy my new house (!). Yes, it's everything, all the time, 24/7 and full after-burners at Chez Howie.

If you like retro arcade-action-puzzle games, check out Rocket Mania! by Popcap Games. This is the other thing keeping me from finishing the Tapestry docs :-)

Saturday, August 23, 2003

Home Stretch on the Book

I'm on the home stretch now for "Tapestry in Action". Manning wants the final version by the end of this month (Aug 2003) ... I'm reconciling all the references to listings, tables and figures, and exporting my ugly diagrams from Visio into TIFF, which is what the production people need. Pretty much, a gruesome, crushing process.

The last set of reviews were pretty positive, except for Ryan Cox who was, like, 95% positive, but really didn't like a few aspects of the book ... didn't like the Hangman example app, didn't like the sidebars and warnings, didn't like the deep investigation of the Virtual Library application. This is part of the process ... about fifteen different people have reviewed some or all of the book, and they disagree wildly. Mind Bridge, Geoff Longman, Greg Burd and Dave Buck think the book is aces, and they are my real target audience so I'm not exactly worried.

Attack Struts! Don't mention Struts! Compare it to Servlets! Don't mention Servlets! More sequence diagrams! Avoid UML! Keep it short! Go in-depth! ... it's maddening. Tapestry is just so big ... and yet, so simple at the core of it. The same little trick repeatedly endlessly, and the only way I know to demonstrate that is to show that same trick again and again and again.

I just had to take a break, so I did some more work on HiveMind. I've added the concept of different "service models" for services; the existing ones are singleton and deferred. Singleton is a singleton service, created at first reference. Deferred is based on some work I did last week; the service creation is deferred; a proxy is created to stand in for the actual service until a service method is invoked.

The new service model is threaded. This is something new and neat; it bypasses the multi-threading problem by allowing each thread to have its own dedicated instance of the service. That is, the proxy will construct an instance of the service that is bound to the current thread (ah, the magic of ThreadLocal). From that point on, any time that thread invokes a service method, it is directed to that thread's service instance. Same proxy passed to a different thread will get a different service instance ... I actually test this as part of the unit test suite.

There's now a service, ThreadEventNotifier, that is used to inform objects when the thread is "cleaned up" (i.e., the end of the request cycle in a servlet or Tapestry app).

I suspect in the future, I'll add additional service models; for instance, one like threaded, but using a pool of service instances ... and maybe one like threaded, but permanently attached to the thread once created.

Thursday, August 21, 2003

Changed my blog template

I changed around my blog template, to move the links and archives over to the right side. Perhaps I was inspired by http://www.csszengarden.com/.

Actually, this is more of an apology; I think that blogger screws up the dates of old postings when I republish my entire site (to update the older archives to match the new template), and some aggregators may see a flurry of false new postings. So be it.

Wednesday, August 20, 2003

Microkernel Mash Up at The Server Side

A thread on TheServerSide (PicoContainer 1.0 beta 1 has been released.) has, like all threads on TheServerSide, devolved into a rambling, biased comparison of everyone's pet projects.

Such as HiveMind.

Interestingly, I was far from the first to mention HiveMind, but I posted a couple of corrections. Also, Drew McAuliffe doesn't like the name. Pshaw!

Tirade against Out-Sourcing

Sue Spielman has written: Outsourcing in my company? I do not think so.

She really sums up a lot of my basic problems with out-sourcing (based on my own experience): it doesn't save money, it's a PR nightmare, it leads to inferior quality and service, it displaces American jobs, and ultimately, it doesn't save money.

For a long time, my Developer's Mantra was mental flexibility and initiative. The flexibility to pick up new ideas, coding or otherwise, and run with them. The initiative to identify problems and address them. There's that JFK quote (I'll have to paraphrase) about seeing things that don't exist and asking "why not?".

None of the off-shore IT help I've ever dealt with has had either mental flexibility or initiative. Whether it's support staff who don't provide support, or "developers" who produce terrible spaghetti code, it just doesn't work.

That's not a slam on those people ... it's a slam against the short-sighted, greedy, mismanaged companies that hired them to perform a job function that they don't have the experience, English language skills, and mind set to achieve.

Sure, I keep hearing "its the future, get used to it" but I'm not buying that ... Eventually one of these big Fortune 500 companies, or Gartner, is going to actually analyze whether out-sourcing really works. Spending 100% for a successful project is always a better deal than 30%, or 10%, for a failure.

I definitely like Sue's advice that if you are expected to train your off-shore replacement, quit. I just hope her company is looking for a talented Tapestry developer when that comes to pass ...

Meanwhile, I need to check what Howard Dean's stand is on this.

Friday, August 15, 2003

Another book on Tapestry

Here's something fun that popped into me mailbox this morning:
Dear Howard,

We will publish the first german book on Tapestry 3
in December / January.

We would like to contact you few weeks before it finishes
and kindly ask you to write a short foreword for it.
Al long or short as you like.

The Software and Support Publishing Company (owns the biggest Java-Magazin in Germany http://www.javamagazin.de/) has 
agreed to this book.

A book similar to ours will look like
this: http://www.software-support-verlag.de/sus/sus_buch/psecom,id,37,nodeid,8,_language,de.html

I am looking forward to hear from you.

Kind regards
Prof. Dr. Stefan Edlich

By the way: You have provided the wold with a great bunch of software!

Java-Magazin recently published an article comparing Tapestry favorably to WebWork written by Christian Sell.

Wednesday, August 13, 2003

Importance of toString()

I'm always dissapointed when I use some else's code (open-source or proprietary -- though its worse for proprietary) and the objects I get back from that code don't implement toString().

One of the main goals of Tapestry is feedback: when things go wrong, provide the developer with as much information as possible to correct the problem. HiveMind follows the same path, crafting useful exception messages, and providing the same line-precise exception reporting as Tapestry. Every object ever made visible to the client code (the code that uses HiveMind) will implement toString().

It's not that hard, folks!. HiveMind and Tapestry mostly use ToStringBuilder from Jakarta commons-lang. I don't really care about efficiency ... toString() generally gets invoked only in a worst-case situation, where the code is already throwing a stack trace or such. What's important is that the developer be provided with as much useful information as possible ... but not overwhelmed with extraneous detail (see my earlier blog about excessive exception logging).

Even the dynamically generated interceptors and proxies in HiveMind get a toString() method; I mean, which would you rather see:

$Proxy_f6f75b1384_25@b6d6ab

or:
<Proxy for hivemind.test.services.BuilderAccess(hivemind.test.services.BuilderAccess)>
The first tells you nothing at all; the second identifies the service and the interface implemented by the proxy. Which is going to kick start your debugging? Next time you write some "reusable" code remember that it can't really be reused unless it can be debugged, and the first step in writing debuggable code is to implement toString().

Tuesday, August 12, 2003

HiveMind Deferred Service Proxies

I'm really coming to love Javasssist. I've called it a giant hammer searching for nails and I am finding a lot of nails to hit with it.

One thing that was bugging me in HiveMind was that if you create a service, and that service uses some other service (that is, you set a property using the <set-service> property setter), then the other service will be constructed. And so on, and so on. Of course, the service could look up the second service just as needed, but that's bad from both the unwanted-coding perspective and the Inversion of Control perspective (or perhaps those two are perfectly aligned)?

In other words, I want to be able to assign the service as a property (of another service) without actually constructing the service. Fortunately, services are hidden behind interfaces, so all I need is a implementation of the service that looks like:

private syncnhronized TheServiceInterface _service()
{
  if (_service == null)
    _service = _serviceExtensionPoint.constructServiceImplementation();

  return _service;
}

public Foo serviceMethod(...)
{
  return _service().serviceMethod(...);
}

And, of course, where does that implementation come from? Brewed up on the fly by Javassist. So now I can have my cake and eat it too ... all the worrying about when to instantiate classes falls back into the HiveMind framework domain, and is of no concern to the HiveMind user, who simply has an interface and is free to invoke methods on it.

Monday, August 11, 2003

Default symbols in HiveMind

There is now an easy way to set "factory default" symbols in HiveMind. There's a new extension point, hivemind.FactoryDefaults. Contributions to this extension point define default values for symbols. The "order" of the SymbolSource contribution is very high (I suppose I should have use MAX_INT, but instead, it's at 1,000,000). This means that, in all likelyhood, whatever additional SymbolSources you add will be consulted first, before FactoryDefaults.

So, you might ask, what the hell are symbols? Symbols are a kind of escape clause; sometimes the information you want to contribute isn't known until runtime (or at least, until the time the application is installed). This might include database URLs or admin e-mail addresses.

What we don't want is to make anyone unpack a JAR and edit the descriptors ... that's just asking for trouble.

Instead, the descriptor includes a placeholder for the value (using the Ant syntax), i.e.

<database url="${database.url}"/>

At runtime, HiveMind will convert database.url into a real string. How? By using HiveMind extension points. hivemind.SymbolSource identifies different objects or services that can be consulted for symbol values, and establishes the order in which checks take place. FactoryDefaults is one contribution here (if you check, you'll see that there's a service to complement the extension point). You can add your own SymbolSources as well.

I can imagine an installer that would present a UI to collect all of these configurable values ... the database URLs and e-mail addresses. The installer creates a properties file and stores it where the running application can access it; a SymbolSource contribution can read the properties file. Tah dah!

Increasingly, new HiveMind features are being implemented in terms of existing features. This is very cool and to be expected.

Sunday, August 10, 2003

Light at the end of the tunnel!

I just uploaded the final appendix for "Tapestry in Action". That means that main writing is complete! Yow!

What does that leave?

  • Revisions from the technical reviewers
  • Final production issues: fixing all the section numbering and converting all the Visio diagrams into TIFF

I am so sick of the book, you can't imagine; but after I've wrapped up these details I can get back to work on the Tapestry reference documentation (you know, the free stuff).

Friday, August 08, 2003

Geronimo Bandwagon

If you've been reading the mailing lists for Apache's Geronimo, you may have seen me there, plugging HiveMind. To me, it seems like a natural ... but there's the JMX and Avalon crowds as well.

I'm actually a bit skeptical of the whole endeavor; if they were starting from scratch, this would be a total disaster ... but to some degree, many of the bits and pieces are already available. Even so, I'm already concerned this is a private party out in public. We'll see if they can assemble a complex project and do it all via e-mail and wiki.

Tapestry Elevator Pitch

"And God said, 'Let There Be Light!' And there was Light. And it was good." But it lacked a clean separation of concerns.

Well, that's not quite right. An elevator pitch for Tapestry is hard because there is so much misinformation about Web Applications. Everyone think's they're easy, becase everyone's doing 'em, right? Developers are so mired down in getting things working with Servlets or Struts they can't imagine that there's a better way. There is. Lately, I've been going with "the easiest choice is the right choice". Or:

Software development hasn't gotten harder in the last decade ... it's outrageously tight "first to market" schedules and ever escalating requirements that have been putting the pain on developers. People think web applications are easy, because everybody's doing them, but there's a lot of enterprise-critical applications being held together with duct tape and chewing gum. There are so many things wrong with the web application development model: that all developers are fully equal, that Java developers can do HTML (or even choose colors), that what works in a prototype will work in a full scale application. Tapestry's component approach takes on all of these issues and more: it lets the Java guys do Java, let's the HTML guys do HTML and lets everyone work together sanely. It provides a standard approach to all the issues, which keeps every developer from becoming the "lynchpin" of their little code-fiefdom. It keeps simple things simple, and makes complex things simple as well. With Tapestry, when you choose the easy option, you aren't screwing yourself in the long run: the easiest approach is usually the best approach, by design. Tapestry doesn't get in the way of developers, it liberates them, helps them, and keeps them focused on the domain issues, and lets them ignore all that annoying plumbing.

Moving code

Last night, I tagged HiveMind as 1.0-alpha-1 and created a branch on Tapestry (hlship-3-1). The branch represents the future Tapestry 3.1 code base. First order of business: move code to HiveMind.

This is important, because in alpha-1 the dependencies are backwards: HiveMind depends on code from Tapestry. That code is boring stuff: IResourceResolver, IResourceLocation, ILocation, and related implementations.

I've moved the interfaces over and stripped off the leading 'I', and moved over implementations and tests. The major changes:

  • IResourceLocation --> Resource
  • ILocation -> Location
  • IResourceResolver -> ClassResolver
  • ILocationHolder -> LocationHolder

Also, moved over ApplicationRuntimeException.

With a little shaving to fit, got the HiveMind code coverage up to 88%, with 112 tests, Tapestry 3.1 needs a little work before it will be functional again, but its close.

Tuesday, August 05, 2003

HiveMind -- progress

I've been adding more and more to HiveMind in the last couple of days. Yesterday, I added several new translators: extension-point, service, enumerated. I'm keeping the documentation up-to date. I just fixed some latent bugs in Javassist code generation. The code coverage is at 87% (pretty good for 93 files and 5,491 ncloc). I've made recovery from module parsing errors more configurable.

I've added a nifty system to translators called "initializers"; this allows you to say something like "int,min=5" to enforce a minimum value when converting an attribute from a string to an Integer. Each Translator implementation may have a second constructor, which takes a string, and can interpret the string (usually as a list of key value pairs).

There's been some discussion on the Tapestry mailing list about HiveMind. Dave Solis wants meaty examples. Colin Sampaleanu doesn't like the registry part, he wants a more pure IoC (Inversion of Control). You can use HiveMind in an IoC mode, especially with the new translators combined with the BuilderFactory, or you can access the Registry to lookup services and extension points in your own code. I prefer to give options. He also had some good points about testing.

I'm thinking of adding some "deferrment" capability to HiveMind. For example, when you obtain a service, you may get an object that implements the interface, but doesn't talk directly to the service. Instead, as needed, it will ask the service extension point to create the instance (and interceptors) ... that is, on first invocation. Likewise, getExtensionPointElements() may return a List and defer the actual creation of the list of elements until first access.

Just talked with my buddy Prashant and another good thing for HiveMind would be a validator ant task; something that could read all the module deployment descriptors and validate that all contributions to all extension points are valid ... that is, recognized elements, recognized attributes, required attributes provided, and so on.

Maven -- Update

I still can't get Tapestry to build under Maven but yesterdays "current peave" appears to be innacurate. I don't know why, but redoing the same steps now seems to work.

Next step is to get my tweak to the clover plugin to actually work.

Update to the update: my maven bulid keeps failing with OutOfMemory error. Don't know why. That explains the discrepency ... yesterday, I wiped out my %MAVEN_HOME% before building and the result was a partial Maven install that fails when building HiveMind.

Monday, August 04, 2003

Maven -- The Emperor has no clothes

I still can't believe that the Maven crew is trying to go to a 1.0 release while Maven is clearly in an early alpha stage. For simple, simple projects like HiveMind, it works acceptibly, but I've had zero luck getting Tapestry working ... Maven just can't handle multiple projects, despite its claims. Maven is a great idea in theory, but in practice, it falls flat. They'll probably get it together eventually, but it is not ready for primetime yet.

My current peave is that I can't build Maven locally (I want to make a minor fix to the clover plugin). Not just pulling down the CVS HEAD, but I pulled down a tagged release, the beta-10, and tried to build locally. Crash and burn. The whole point of Maven is that this should not happen, I have the same code as they did when they rolled out the beta-10 distro, and all the dependencies line up because they come out of the central repository. Either Maven has some hidden dependencies and doesn't like JDK 1.4 on XP, or they messed up with the tagging, or Maven just doesn't work as advertised.

Meanwhile, the problem with Maven is that it adds many, many layers of indirection on top of Ant. Although the output is pretty, and some of the reports are cool, unless things change soon, I'll be back in the Ant camp --- the 1.6 release of Ant promises to have a lot of the things I'm missing, like a rational way to reuse build files across a large project.

Friday, August 01, 2003

More fun HiveMind stuff

Getting HiveMind to work with Vista (the WebCT product) is going well. I immediately found that I needed <schema> to support an id and ref-id attribute, to make it easy to re-use a schema within a module. Vista has Startup, PostStartup and Shutdown extension-points with long, but identical, schemas.

I think the code and general appraoch is stabilizing. Things to do:

  • Validations of extension contributions. Be nice to have equivalents to W3C Schema minOccurs, maxOccurs, <sequence> and <choice>
  • More XML rules and translators
  • More base services

Still, that's pretty cool. HiveMind is turning into a powerful bit of code ... right according to plan.

Thursday, July 31, 2003

HiveMind going strong

HiveMind stuff is going strong; just brought the documentation up-to date and continued cleaning up things and simplifying interfaces.

Something that just went was the class attribute on the <module> element. The idea was to allow subclassing of the module to provide a common location for code. This kind of parallels what you would do in an Eclipse plugin. In HiveMind, it isn't necessary ... you'd just create a service and put the common logic there. So there's no need to allow subclasses of the base Module class (was BaseModule, now its ModuleImpl).

I'm definately looking forward to merging Tapestry and HiveMind together (that will form Tapestry 3.1 and HiveMind 1.0-beta).

Wednesday, July 30, 2003

Whew!

Humpty dumpty is back together again. I've reformulated/refactored/reinvented HiveMind as discussed in the previous few 'blog entries.

Still need to catch up on documentation and the XSL for creating HiveMind registry documentation.

Code coverage is around 84% and I haven't even tried to fill in the gaps yet.

I was able to streamline the key interfaces (Module, ServiceExtensionPoint and ExtensionPoint) quite a bit by moving the logic that caches service implementations into ServiceExtensionPointImpl, and the logic that caches extension point elements into ExtensionPointImpl. Looks better.

HiveMind seems a bit faster now ... it's hard to say, because the test suite has changed quite a bit, but I think the new SAX parser is faster than Digester ... one less layer: a lot less logic to figure out which "rule" to fire in response to a given SAX startElement() call. Also, most of the time, the DescriptorParser can complety ignore character content provided via characters() ... doesn't have to stuff it into a StringBuffer just to throw it away later.

Version Numbering

Here's an article about how some projects mis-use version numbering: The Fishbowl: Version Numbers and You

Tapestry and HiveMind follow a different cycle, based on a major version number and a minor version number, i.e., 3.1. Major version 3, minor version 1. Major versions don't happen very often, since they represent a near-total rewrite of the framework. Minor versions change in an orderly fashion, representing incremental improvements. In general, we want to maintain backwards compatibilitily from one minor version to its predecessor, but all bets are off when the major version changes.

A change to minor version represents a new release, which has its own lifecycle (and takes several months). Releases go through stages:

  • alpha - code coming together, features being added, removed, documentation nowhere, too chaotic to use for real work. Additionally, deprecated code from previous releases may be stripped out.
  • beta - things setting down, the goal is to stop adding features, stabilize and document
  • rc - release candidate; exposed to a wide audiance so as to find any final bugs
  • GA - General Availability - the final version that people should use.

So for each release, there will be a number of incremental versions, as the code goes through these cycles. For example: 2.3, 3.0-alpha-1, 3.0-alpha-2, 3.0-beta-1, 3.0-beta-2, 3.0-beta-3, 3.0-rc-1, 3.0, 3.1-alpha-1.

It's possible, though we've avoided it so far, to "double dip"; for example, drop from beta back into alpha.

This system works well; the older numeric system (from the 1.0 and early 2.0 days of Tapestry) made it hard to tell how stable the code was, now the stability (alpha, beta, rc) is part of the name (and thus, incorporated into the distribution and even the names of the library JARs).

I adapted this naming sequence from somewhere, can't remember exactly where. I find it much more managable than a fully numeric system (quick: how stable is 3.7.9.2.1?) The code goes through known stages (we do a vote on each stage transition). I've been encouraging other projects (such as OGNL) to make use of this same naming system.

Monday, July 28, 2003

Improving HiveMind naming conventions

Because I'm working to make HiveMind feel a bit more like Eclispe plugins, I'm beginning to rename things to be more consistent.
  • <configuration> --> <extension-point>
  • <contribute-configuration> --> <extension>
  • <contribute-service> --> <extend-service>

In addition, I'm scrapping "internal contributions" to extension-points. Instead, we'll allow <extension> to use an unqualified name for an extension point that's within the same module.

ApacheCon US 2003

Per Andrew C. Oliver's suggestion, I've submitted to present a session on Tapestry at ApacheCon US 2003. It'll be a challenge doing anything meaningful in 50 minutes ... I may need to request two consecutive sessions. I already have most of the materials I'll need, since I've been working on an actual multi-day Tapestry course.

Thursday, July 24, 2003

Thoughts on Exceptions

Everyone has their own approach to exceptions. Here's a few of mine.

Runtime vs. Checked

Checked exceptions should be used only when they will be explicitly caught and code paths will diverge based on the type of exception. This is, in fact, extremely rare (and some JVM code violates this pattern). For example:

try
{
  foo.bar();
}
catch (FooFrobbedException ex)
{
  // recover

  gnipGnip(foo);
}
That's where you want to use a checked exception. Generally, these situations only occur along software boundaries, usually between the code-you-write and the code-you-got (from a library). Questions to ask: can this be recovered from? and will anyone catch this?

Catch and Wrap

This again occurs along boundaries; often you'll catch someone else's checked exception, and (since there is no recovery), rethrow it (in a runtime exception). What's essential is that you keep a reference to the original exception. I tend to call this a "root cause". Tapestry exceptions have been doing this forever; JDK 1.4 has added the ability to attach root cause exceptions consistently (in a somewhat ugly way to maintain backwards compatibility). In JDK 1.3 and earlier it was hit and miss.

Why is this important? Because as you catch and throw, you are losing information; a SAXParseException may identify the public id, line and column but as it goes up the stack, that information is lost; we lose the line and column when we say "can't parse foo.xml", and we may lose even that amount of context when we throw an exception that says "can't accomplish task foo".

Add information

If you are going to catch an exception and rethrow it, add information along the way. Extend the exception message with more information, to provide more context. Add additional read-only properties to the exception to identify more information. The point of this wrapping and rethrowing is that an uncaught, top-level exception provides you with the tools to identify the bug and fix the problem.

Log it or not?

In some application's I've worked on, every time an exception occurs, it gets logged as an ERROR. Of course, that exception gets wrapped and rethrown ... and caught, and logged and rethrown. The end result is thousands of lines of meaningless stack trace scrolling by, which only muddles things ... it doesn't help track down the problem or even the source.

The right approach: catch the exception, and log it, as DEBUG (or at most WARN) before wrapping and rethrowing it ... but only at code boundaries. These are those same code boundaries between, typically, your applications code and library and framework code you've got or bought. That's the only interesting place. Why not ERROR? Because you have top level reporting (next item).

Top level exception reporting

This is the most important part of exception handling ... what to do when nobody catches and handles the exception. It all comes together here, something Tapestry has excelled at since before 1.0. When a top-level exception makes it to the top loop (in Tapestry's case, to the Engine's service() method) you want to produce as much information as possible.

Tapestry's approach has been to identify the stack of exceptions, starting with the outer-most one and working inwards. Each exception's class name and message is displayed, along with all readable properties. Along the way, it finds the next innermost exception and does the same thing ... down, down, down to the deepest exception. That's where the stack trace is displayed.

Tapestry includes a utility class, ExceptionAnalyzer, for just this purpose (it may move to HiveMind in the future).

More JSF propaganda and Tapestry counter-propaganda

OnJava article: Why Web Developers Need Java Server Faces [Jul. 23, 2003]

As usual, the article doesn't bother to make the case for JSF beyond vague allusions to events and MVC.

Tapestry collaborator Mind Bridge has told me he thinks JSF has gone from threat to friend; JSF validates the concept of web application components (in the minds of the majority who are ready to drink whatever special kool-aide comes out of Sun and the JCP), but delivers so dismally that people will be forced to find an alternative: Tapestry.

Kind of thrashing

I've been kind of thrashing on this whole schema/DFA/validator thing. I decided (in the shower, where I do all my best thinking) to temporarily abandon formal validation, and concentrate on everything else.

Over the last couple of days, I've been using the existing HiveMind with Vista (work project), where I've been setting up all the startup, post-startup and shutdown tasks for the server as HiveMind services and contributions.

It's working like a charm, but is too verbose; contributions look something like:

<new>
  <set property="title" value="Cache Initialization"/>
  <set property="order" value="100"/>
  <set-create property="runnable" class="com.webct.vista.framework.cache.CacheStartup"/>
</new>

Under the new system, this would look more like:

<task order="100" title="Cache Initialization">
  <runnable class="com.webct.vista.framework.cache.CacheStartup"/>
</task>

And its only that complex because I need a few different ways to define what to execute (the existing code is based on invoking static methods on classes, so I currently have a syntax for accomplishing that using reflection).

Sunday, July 20, 2003

More thoughts on improved HiveMind

Part of this new design for HiveMind is a change to implementation factory parameters. These will also be a schema ... basically, each implementation factory will have a kind of implicit configuration extension point to define its parameters.

This is important, because all the module deployment descriptor tags related to setting of properties are going to fade away; if they come back, they will be reimplemented as specific to a implementation factory service.

I'm actually struggling a little bit with the idea of processing the XML of configuration contributions (and factory service parameters). I'll be emulating a subset of W3C schema give-or-take, and dealing with that is a bit of work. Just need a little block of time to think about how to approach it properly, with all the kinds of variations that are possible (in terms of sequences, choices and setting bounds on the number of occurances for an element, choice or sequence). It's a little frustrating because its certainly a problem that has been solved repeatedly by others ... and, I can look at their code, but it may just be eaiser to hash it out myself.

Friday, July 18, 2003

Philosophy of configuration contributions to HiveMind

A lot of the fancy stuff in HiveMind is in HiveMind services, but the meat-and-potatoes are in HiveMind configurations.

A configuration extension point in HiveMind is a container to which Java objects are contributed. The extension point defines the "flavor" of objects that may be contributed in terms of a Java class or interface that contributions must be assignable to. It may also defined either a factory service to create new contribution instances, or define a class to be instantiated (this is to support <new> in contributions).

This is a very Java-object centric approach. Unlike Eclipse plugin contributions, which are very XML based (you use a subset of XML schema to define the "flavor" of contributions in Eclipse), you are really just providing instructions on how to instantiate and configure/assemble contribution objects.

A debate here at WebCT (I'm developing HiveMind as general purpose, but to fit the needs of my employer) is whether this is appropriate.

One contrary suggestion is to move closer to Eclipse plugins; that contributions should be expressed as XML and the configuration extension point should be responsible for converting them from XML to Java objects.

If you look at the Eclipse code, plugins have to waste a good amount of code walking XML elements and attributes to deduce what the content of the extension point is. HiveMind client code, the code that gets the contents of an extension point, just has to cast the elements from the List to the right type. I really don't want to have to change that ... that's a lot of potentially buggy code to write, just to access the elements of the configuration.

But what if the extension point included a kind of schema for contributions, and the schema mixed in some ideas from Digester about how to process contributions? We'd have to write our own souped-up digester capable of adapting its rules on the fly. Anyway, I can then imagine a configuration extension point looking something like:

<configuration id="SymbolSource" element-type="org.apache.commons.hivemind.SymbolSourceContribution">
  <description> ... </description>
  <schema>
    <element name="symbol-source">
      <rules>
        <create class="org.apache.commons.hivemind.SymbolSourceElement"/>
      </rules>

      <attribute name="order" type="int" required="false">
        <rules>
          <set-property property-name="order"/>
        </rules>
      </attribute>

      <attribute name="class" type="org.apache.commons.hivemind.SymbolSource">
        <rules>
          <instantiate-instance property-name="source"/>
        </rules>
      </attribute>

      
    </element>
  </schema>
</configuration>

Obviously, this is a lot more work for the person defining a service extension point, but it means that contributions to the extension point are more succinct and readable:

<contribute-configuration id="org.apache.commons.hivemind.SymbolSource">
  <symbol-source order="100" class="foo.bar.Source"/>
</contribute-configuration>

The <rules> element contains Digester-like rules for constructing objects, setting properties, connecting parent and child objects, as so forth.

In addition, we can attach a description to individual elements and attributes, which is nice.

Of course, there would be analogs for more of the simple XML Schema elements, such as sequence and choice. This is just a bit of a sketch, there are many details to be filled out and its quite a programming challenge.

Other artifacts within a module specification will also need a <schema>, such as a service implementation factory (to define what may be contributed inside a <parameters> element).

There are a lot of outstanding issues, though:

  • How to cleanly handle contributing a reference to a service (much like <set-service-ref> in the current design)?
  • Likewise, other special types: localized messages and OGNL expressions
  • Even more difficult to generate documentation from this style, since we won't know the types of elements ahead of time.
  • Does the idea of configuring an object instance (say, a core service implementation) make sense? Will we also have to go through a special factory service?
  • Will this significantly affect performance?
  • We will have to keep more of the content read from a module descriptor in a DOM format; we can't go right to descriptor objects anymore, because we need the contribution and the schema for the configuration extension point at the same time, which we don't necessarilly have until all module descriptors have been parsed.

I want to keep noodling on this; it feels like it is a step in the right direction. It means, though, that we have to abandon having an XML Schema for HiveMind, since the content of a HiveMind module descriptor will be very free form. I suppose that this could be addressed using namespaces, but the only advantage to using a schema is the use of a validating SAX parser, and we'll have to be doing our own validating anyway, so we can at least generate some better error messages for failures!

Thursday, July 17, 2003

HiveMind updates

HiveMind continues to come along nicely; I'm quite proud of it.

The latest addition was a revision to how parameters to service implementation factories work. A service implementation factory is a HiveMind service used to construct the implementation of some other HiveMind service. Down the rabbit hole yet? This is occasionally useful ... the example built into the HiveMind framework is creating a HiveMind service that fronts for a stateless service bean. The implementation that is generated (in this case, using JDK dynamic proxies) looks up the home interface in a naming service (a HiveMind service wrapping around JNDI), invokes the create() method (using reflection), and forwards all the remaining interface methods to it.

The factory needs to know the JNDI name. The new approach is that the factory will create a parameters object and the <parameters> element of the descriptor will configure the properties of the parameters object, which is then passed back to the factory so that it can construct the final service.

Why not just set properties on the factory service implementation itself? Because the factory service implementation is a HiveMind service, and therefore it is multi-threaded. Two different threads could be trying to create implementations for two different session EJBs at the same time.

It ends up looking something like:

<service id="SimpleRemote" interface="hivemind.test.services.SimpleRemote">
  <service-factory service-id="org.apache.commons.hivemind.EJBProxyFactory">
    <parameters>
      <set property="jndiName" value="hivemind.test.services.Simple"/>
    </parameters>	
  </service-factory>			
</service>

Ah! Blogger seems to have come back to life after a long absence.

This is very much good news; the Tapestry Blog at Freeroller is just too unstable to use. I guess from here on in, I'm back home in Bloogler land.

I'm using this blog for both Tapestry and HiveMind because eventually, much of the Tapestry code base will be moved into HiveMind, and much of Tapestry will be re-architected to take advantage of it.