[TransWarp] Configuration/binding refinement
Phillip J. Eby
pje at telecommunity.com
Mon Jul 8 21:22:50 EDT 2002
At 10:33 PM 7/7/02 -0400, Phillip J. Eby wrote:
>What we end up with, instead, is a simple interface, something like
>getConfigSetting(propertyname), that's available on all
>binding.Components. The default implementation would look things up in a
>dictionary associated with the component, and if not found, delegate the
>inquiry to the parent component, unless there is none, in which case
>request a lookup of the default configuration object, and delegate to
>that. Components at each level can potentially cache their response.
>
>...
>
>I haven't given much thought to this style sheet idea as yet, and I don't
>plan on trying to have it all figured out before implementing a basic
>configuration-acquiring interface. But I do want the initial system to
>support a very limited sort of "pushing" of configuration settings: a
>component should be able to define a binding for one of its attributes
>that specifies bindings for parameters of the child object. Right now,
>the only way to do this is to subclass the child object as a nested class
>in the parent object's class. It would be nice to be able to specify
>configurations that will apply, regardless of the actual class that ends
>up filling the spot. This is important for the naming system in
>particular, where the exact class of Context object is not going to be
>known ahead of time, even though the component that needs the context
>knows what settings it wants to supply to that context.
It looks like the simplest way to implement all of the above will be by
keying configuration lookups by one or both of an interface and a name, or
perhaps some sort of parameterized interface. The result would be that you
could do the equivalent of Zope 3's "getUtility()" to acquire an object of
a specified interface.
Components would provide these "utility" objects either as a shared
instance owned (or weak-reference cached) by the providing component, or as
a factory which would be called to create a new instance as a component in
the context of the consuming component. For example, a "data model" might
provide an IDBConnection utility as a shared instance, but provide an
IDBCache utility as a factory which created a distinct cache for each of
its sub-components needing a cache.
More "traditional" configuration usages, such as setting simple values like
filesystem directories, e-mail addresses, etc., would be implemented by
defining an interface for a collection of related configuration data,
implementing an object that supplied that interface, and then registering
it as a provided utility.
If we can support parameterized interfaces, then this mechanism can be used
to replace all sorts of registry mechanisms. For example, URL context
factories and URL parsers could simply be registered in context as
implementations of their respective interfaces, with a parameter indicating
the URL schemes they supported. The lookup operation would specify a query
to be applied to the parameter information, perhaps using a variant of the
existing "peak.metamodels.querying" package.
There are a few things that would need to be sorted out in order for all
this to work well. Mainly, the issue of precedence and overrides within a
specific registry at the component or application level. If lookup
criteria result in more than one possible match at a given level of the
hierarchy, how is the winner decided? What if a new provider is registered
after an old one has already been used?
A simple rule to fix the precedence is that we could make items loaded
later have lower precedence, which would make the behavior consistent, and
would make configuration data "include'd" from another configuration file
be a default, automatically overridden by the more specific, earlier-loaded
file. It's also easy to implement as a list.append() operation!
There is only one problem with this rule, as far as I can see, and that's
that we want dynamic configuration to take precedence over static
configuration. This could probably be dealt with by having instances'
registry attribute binder issue a request for dynamic configuration at
binding time, before incorporating any class-defined registration.
Interestingly, this could be done by treating the utility registry as
itself being a utility! The root-level configuration system could provide
a utility factory that would look at the component which was asking for a
utility registry, and hand it one pre-loaded with configuration-supplied
data...
You know, I think we have a winner here. There's still a little bit to
work out about how the interface lookups need to work, and we need to
define an interface for utility registries. There's also the question of
whether utility registries should cache values returned from higher-level
registries. I think they can cache instances created above them, or
factory functions for creating things below them, but not the result of
calling a factory function. There will also need to be some metadata for
indicating whether a utility instance should be held with a weak reference
or not. Some objects you'll want to have only hang around as long as
they're in use, others you want to have live indefinitely. Last, but not
least, the parameter and query mechanisms will need to be defined along
with the interface.
More information about the PEAK
mailing list