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!

Sunday, July 18, 2004

HiveMind and autowiring

Sometimes the function of a community is to force you in the right direction. With HiveMind, there's the ongoing concern of too-much XML. Erik Hatcher somewhat forced the issue (he can be very incisive) and the response was the controversial Simple Data Language.

However, even that may not have been enough. The SDL is more concise, but your still have a lot to say in it. Over time, the BuilderFactory has been extended, in a number of ways to decrease the amount of SDL you need to put in place. For example, if a service needs to know its service id, it just needs a setServiceId(String) setter method and BuilderFactory will invoke it.

My more recent changes build on a very nice patch supplied by Marcus Brito, which allows you to access a service within the Registry using just its service interface. HiveMind will find the lone service point that implements that service and return it (throwing an exception if no service, or more than one service, implements the interface). Marcus did a good job, supplying a nice patch including JUnit tests (I only needed to do some minor touch ups). I can see how useful the approach is, though there's the danger of it causing problems (what happens at a later date when a second service is introduced?).

I've introduced another form of autowiring into BuilderFactory: after it has set all the properties it knows about (via autowiring and expicit sets) it checks the remaining writable properties. Any such properties that are interfaces are assumed to be services, and the unique service for that interface is located. Again, errors abound when the interface isn't a service, or there is more than one service implementing the interface (an attribute allows autowriting of services to be disabled).

The upshot of this is that you can specify less and less in the module deployment descriptor, and HiveMind will still do the right thing (or, it will provide detailed feedback about what went wrong).

The other big change in HiveMind of late is the concept of object providers. This is (as with much in HiveMind) somewhat abstract but very exciting. Previosly in HiveMind, the schema format identified the type and interpreation of attributes using translators. Is an attribute a configuration id? Then use the configuration translator. Is the attribute a service id? Use the service translator.

This is good as far as it goes, but it tends to promote a kind of propogation of elements. Look at BuilderFactory, which has a bunch of elements for different types: set, set-long, set-service, set-configuration and so forth.

This is also limiting; a property of an object (or service) to be set may be an interface, and it doesn't matter to the object whether that actual instance plugged in is a service or an arbitrary object. Given factory services (such as BeanFactory), its evident that HiveMind services aren't the only interesting objects out there. That's a problem that forces some people to cut-and-paste BuilderFactory to add new ways of supply objects to the constructed service. That's not good!

This is where the new object translator comes it. The translator is driven by a configuration point, ObjectProviders. The configuration defines different prefixes and provides services that know what to do with strings with that prefix.

So, for example, service:MyService will be intepreted as a service id, and bean:MyFactory:name will access a BeanFactory service and ask it for the name bean. There's even a service-property:MyService:propertyName option, to access a service and read a property from it.

For building services, BuilderFactory now has a set-object element. Further, since this is driven by a configuration point, it will be possible to add application-specific prefixes as well! So, once again, HiveMind is object soup, and its getting less important where those objects are coming from.

4 comments:

Ross Judson said...

SDL looks like a quarter step towards a proper LISP syntax. XML sucks as a language for configuration files. You've got several goals: You want your configuration information to be readable, concise, and as flexible as possible.

The problem with configuration file systems (and systems like Ant) is that to reduce the size of the resulting files they embed primitives that do more and more. Ant has commands that do lots of different things. Most configuration XML files are the same thing again.

What we all really need is for these configuration files to be written in a proper programming language, so that someone writing a "configuration file" actually has access to the real mechanisms for handling complexity and achieving flexibility and readability. We need to push back on the desire to treat configuration data as simplistic data -- it is not.

The following probably won't paste nicely, but I point it out because it would be very simple to create a few Scheme macros that would allow the following to be valid syntax. With a number of open source Scheme interpreters of varying degrees of sophistication available on the JVM, I think it makes sense to give a programmer a real language to work with.

I suspect that a dramatically simpler configuration syntax can be constructed in Scheme, as well.

(module ((id some.module)(version 1.0.0))
(configuration ((id ControlPipeline))
(schema
(element ((name processor))
(attribute ((name name)(required #t)))
(attribute ((name service-id)(required #t)(translator service)))
(attribute ((name before)))
(attribute ((name after)))
(conversion ((class some.module.PipelineContribution))
(map ((property controlService)(attribute service-id))))))))

Howard said...

Oh, yes. What we need is Scheme to hide the fact that we're using XML in a different syntax. No ... we should be using YAML. Scratch that ... RDF. Whatever. None of that addresses the core issues.

There's an ongoing discussion about whether HiveMind module deployment descriptors should be declarative (as they are today, in whatever syntax you choose) or procedural (scripting, using BeanShell or Groovy or flavor-of-the-month).

I'm pretty dead set against procedural; once you go down that path, HiveDoc becomes completely useless. I think the approach I've outlined in this blog ... making smarter use of code to eliminate the amount of content in the module deployment descriptors, is the way to go.

Ross Judson said...

Hmm. I don't think it's really a simple matter of syntax.

(module ((id "something or other"))

(define (java-application identifier main-class comment)
(configuration ((id identifier))
(class main-class)
(...a bunch of stuff)))

(java-application "first" com.Go "something")
(java-application "second" com.Go "something"))

This doesn't really have to have anything to do with driving an application's logic; this code merely assembles the exact same configuration data that something like SDL would take much more space to do. It's more concise, and more readable as a whole. My simplistic example there builds a quick function that can act like a template. It's nice to have a real language at your disposal. Variable substitution and simple keywords are a weak replacement.

It can be considered to be procedural construction of configuration data. It does not necessarily have to be part of the procedural vs. declarative argument for application configuration.

Howard said...

Yes, but as soon as you apply that kind of scripting to the MDDs, HiveDoc goes out the window.

If you like that syntax ... fine, write it and have Ant convert it to XML or SDL for you and be on your way.