Registries in general
The registries in ADEPT2 are the central point to get a component for
usage. Registries manage the instantiation, configuration and the
retrieval of used instances. An application programmer can just use a
component (instance) after retrieval.
There are several registries in ADEPT2, for instance, a client-side
registry (RuntimeRegistry), a registry for server components, one for
model factories. All of these handle components but differ in some supported
functions. They are organised in a class hierarchy in which the higher
registries are more generic than registries down the hierarchy.
Plugin types for components
Every component instance can declare plugin types, which it is able to use.
At runtime the component instance can request a plugin instance of such a
configured plugin type. The plugin instance is either specified by its name
or the default instance is be used. The plugin type as well as the requested
plugin instance have to be declared as separate
component type and
component instance
just like other instances too. The declaration of plugin types just
establish a connection between a using component instance and a used component
type.
Configured usage relations for components
While for plugin components, a specific plugin type and a corresponding instance
have to be requested at runtime, usage relations between autonomous
ADEPT2-components may also be completely encapsulated by the configuration. It
can be configured, for instance, that a process manager instance PM1
uses one log manager instance LM1 for execution logs and a data manager
instance DM1 for the data produced and consumed in the processes.
The data manager itself may use another log manager instance LM2 for
logging data access.
This is all done via the corresponding registry configuration. Just like the
plugin declarations, the
component types
need to be registered, the
component instances
have to be declared as well as the usage relations. See the
example below.
Configuration
Managed components need to be registered at a registry. This is done via
the configuration of the corresponding
registry. By specifying the
mandatory keys
Components.<ComponentType> and
<ComponentType>.Implementation
the corresponding interface and one implementation are registered for a
component. Additionally one can specify:
- <ComponentType>.InstantiationMode
Per default, every component instance is singleton. This means, there
is only one instance per system. If this is not the desired behaviour,
the instantiation mode can be explicitly set to OFF, SINGLETON
or MULTIPLE. If set to MULTIPLE, every request for a
component instance returns a new object instance. This implies having
multiple object instances with the same component instance name.
- <ComponentType>.<UsedComponentType>
If a component uses another component and the used component should be
retrieved via the registry, the instance name of the used component
can be configured. The denoted name for the used component instance
has to be declared. Per default the used component instance is the
anonymous instance. If this is the desired behaviour, the relation
does not need to be configured.
The declaration of component instances requires the
mandatory keys
Instances.<ComponentType>
for several instance names and
<InstanceName>.Implementation
for specifying the corresponding implementing classes. As for component
types, instantiation mode and used components may be specified for every
(using) component instance:
- <InstanceName>.InstantiationMode
This has the same meaning as the instantiation
mode for component types.
- <InstanceName>.<UsedComponentType>
This has the same meaning as the usage
relation for component types (for the anonymous instance). If <InstanceName>.<UsedComponentType>
does not exist, the property for the anonymous instance of the using
component will be checked
(<ComponentType>.<UsedComponentType>). If this does also
not exist, the anonymous type of the used instance will be
used.
The following
example
declares process manager, data manager and log manager instances and the
corresponding usage relations.
# declaration of type "ProcessManager"
Components.ProcessManager = de.aristaflow.adept2.core.processmanager.ProcessManager
# implementation for anonymous instance of type "ProcessManager"
ProcessManager.Implementation = de.aristaflow.adept2.core.processmanager.transientprocessmanager.TransientProcessManager
# declaration of instance "PM1" of type "ProcessManager"
Instances.ProcessManager = PM1, PM2
# used implementation for "PM1"
PM1.Implementation = de.aristaflow.adept2.core.processmanager.defaultimplementation.ProcessManager
# PM2 uses the same implementation as the anonymous instance, no entry necessary
# used data manager instance for anonymous instance of type "ProcessManager"
ProcessManager.DataManager = DM1
# PM1 uses the same data manager as the anonymous instance of type "ProcessManager", no entry necessary
# used data manager instance for "PM2"
PM2.DataManager = DM2
# used log manager for instance "PM1"
PM1.LogManager = LM1
# the anonymous instance as well as PM2 use both the anonymous instance of the log manager, no entry necessary
# declaration of type "DataManager"
Components.DataManager = de.aristaflow.adept2.core.datamanager.DataManager
# implementation for anonymous instance of type "DataManager"
DataManager.Implementation = de.aristaflow.adept2.core.datamanager.dbimplementation.SQLDataManager
# declaration of instance "DM1" of type "DataManager"
Instances.DataManager = DM1
# used implementation for "DM1"
DM1.Implementation = de.aristaflow.adept2.core.datamanager.dbimplementation.DbDataManager
# used log manager instance for "DM1"
DM1.LogManager = LM2
# declaration of type "LogManager"
Components.LogManager = de.aristaflow.adept2.core.logmanager.LogManager
# implementation for anonymous instance of type "DataManager"
LogManager.Implementation = de.aristaflow.adept2.core.logmanager.defaultimplementation.DefaultLogManager
# declaration of instances "LM1" and "LM2" of type "LogManager"
Instances.LogManager = LM1, LM2
# used implementation for "LM1"
LM1.Implementation = de.aristaflow.adept2.core.logmanager.database.DBLogManager
# used implementation for "LM2"
LM2.Implementation = de.aristaflow.adept2.core.logmanager.file.FileLogger
Retrieval of component instances
Required components can be retrieved by requesting them from a registry.
Specific registries provide components type-safe. Instances for plugins must
be explicitly requested by the corresponding name, but one may also use the
configuration for specifying the usage relation between components. In this
case when asking for a specific component type, a using component only
needs to provide its own hierarchical name (which was supplied to it in its
constructor). The registry will resolve the names and return the configured used
instance. In the example, the process manager
PM1 will retrieve the data manager DM1 by simply calling
ServerRegistry.getDataManager("/ProcessManager/PM1"). This way, plugins
are used mainly for retrieving dynamically set component instances, for
instance, a worklist manager retrieves a specific escalation handling from the
corresponding worklist item. If this dynamic is not needed, defining the
usage relation in the configuration will be easier to use for the component.
Component instances are usually created when requested. In case of a
singleton instance, an already existing instance will be returned.
Creation is done by the registry by calling a constructor with an
instance name (String) and a configuration (Properties)
as parameters. A component has to provide an appropriate constructor.
The registry (resp. the underlying ConfigurationManager) takes care of
the configuration for an instantiated component. Thus, a registry encapsulates
the instantiation and the configuration of a component. A component only needs
to parse its configuration.
To prevent multiple instantiation of singletons in multi-threaded environments
instantiation takes place with a lock. Therefore constructor methods should be
as short as possible. Additionally, in a constructor only the executing thread
should access the registry, a newly created thread accessing the registry may
lead to a deadlock. A deadlock may also happen due to cyclic dependencies
between component instances when one component tries to retrieve the other
component while both are created.
Registry types
There are also registries which support complex startup and shutdown
procedures for components, for instance the ServiceRegistry. This works
only with components supporting these sophisticated features
(de.aristaflow.adept2.base.service.ADEPT2Service).
Since the various registries are also components, the loading of the
registries is carried out the same way as other components are loaded.
This is done in the bootstrap registry, which takes care of initialising
logging and loading further registries. The corresponding configuration
is in Boot.properties.