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!