A container a provides the environment in which components may operate. It instantiates the components, connects them together (this is called compositioning), manages their lifecycle (notifies them about system startup and shutdown), provides them with logging facilities and configuration. The container may also provide run-time administrative functionality, from log verbosity adjustments, through component reconfiguration and JMX-enabled components management to aspect weaving/unweawing and component implementation hot swapping.
ObjectLedge container builds on the excelent PicoContainer and NanoContainer packages. Thanks to their flexible design, we were able to create a number of extensions necessary to implement configuration and logging facilities of our container that can be seamlessly plugged into Pico/Nano.
The composition of components is described using an XML file. We are using a custom
implementation of the org.picoextras.script.xml.XmlFrontEnd that is
a bit more expressive than the one found in NanoContainer (but we'd love to see our changes
folded back to Nano!) See the extensions page for
more details.
The components are configured using separate XML files. This makes locating the settings
of a particular component easier than with a single XML or properties file. It is also
more friendly to revision control systems, splitting the configuration into semantically
distinct items. All of the configuration files are subject to schema checking in order to
detect configuration problems early, and without the need to write boiler plate java code.
We have chosen RelaxNG schema language, because it is
compact and easy to read. We use simple naming scheme to associate the component with it's
configuration file and the configuration schema. If a component class name is
com.wombat.Printer the configuration file name is
com.wombat.Printer.xml and the schema file path in the classpath is
com/wombat/Printer.rng. To be more specific, the containter derives the
names from the role of the component in the system. In simple cases it is equivalent
to the component's class name, but usually it is the name of an interface the component
imlements, that the other components depend on.
The configuration of a component, read from the XML file is passed to the component as an
instance of org.jcontainer.dna.Configuration interface. All you need to do
is to declare a parameter of that type in your component's constructor.
A similar facility is provided for logging. If your component declares an
org.jcontainer.dna.Logger parameter in the constructor, it will be passed
a logger instance. All the loggers are configured by a single component, the
LoggingConfigurator,
that will also provide runtime verbosity adjustment facilities in the future.