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!

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!