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: 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: 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.