In an attempt to simplify Rice configuration and improve startup time, we want to look into combining the separate Spring contexts into a single Spring context.
This will likely involve a refactoring of how the "Configurer" objects in Rice currently work.
org.kuali.rice.core.config.RiceConfigurer class is a Spring bean which is responsible for initiating all of Kuali Rice. It has various properties that can be set on it to control how Rice is configured, including:
- data sources
- transaction managers
- configuration (properties, Config object, etc.)
- root resourceloader
- list of modules
One of the properties that can be set on the RiceConfigurer is a list of modules. Each of the modules implements
LifeCycle and extends the
This is used to invoke
stop() methods on each ModuleConfigurer and to load the
Config for each module.
It is assumed that the programmer writing the Spring configuration for RiceConfigurer understands that the ModuleConfigurers need to be injected in the proper order in order for everything to work properly (i.e. KSBConfigurer before KEWConfigurer since KEW depends on it).
The Module Lifecycle
RiceConfigurer runs through a 2-step process as part of it's startup:
- Load Configuration internally and for each ModuleConfigurer
- Start each ModuleConfigurer using it's LifeCycle.start() method
The RiceConfigurer has 2 primary responsibilities that result from the 2-step process:
- Ensure that the Kuali Rice configuration system is properly initialized
- Ensure that the Kuali Rice Resource Loading layer is properly initialized
In the case of the ResourceLoaders, it is assumed that each ModuleConfigurer loads and initializes it's own ResourceLoader and registers it with the GlobalResourceLoader. Typically, each of these ResourceLoaders is backed by a Spring context for that particular module which translate ResourceLoader.getService(...) calls into appropriate service lookups in Spring.
Part of the RiceConfigurer's responsibility is ensuring that the Rice Configuration system is initialized properly. In order to do this it needs to load the configuration from all modules (via
ModuleConfigurer.loadConfig(...)). It then merges the configuration properties returned from each of the configurers with it's own root Config object. Once all configuration has been loaded and merged it executes the static
Config.init(...) method to establish the global configuration for Rice.
Additionally, each module typically has some properties which can be set directly on it's ModuleConfigurer (such as DataSource objects, tx Managers, etc.). There is a step in each of these configurers during the startup process which translates these explicitly set configuration values into configuration properties on the module's
It is possible to inject a RootResourceLoader into the RiceConfigurer itself. In this case the given ResourceLoader will be put at the very top of the resource loading stack after all other Rice Modules have initialized.
There is nothing specified in the contract of ModuleConfigurer which requires a module to do anything regarding ResourceLoaders. However, in order for services loaded for a particular module to be made available to the rest of Rice, each module must register it's ResourceLoader with the GlobalResourceLoader after initialization is complete.
This behavior is critical in the current implementation since some ModuleConfigurers may attempt to access services from other modules upon which they are dependent via the GRL during startup. An example of this is
org.kuali.rice.ksb.messaging.KSBExporter which is used by KEW during startup to expose services on the service bus.
The configuration of OJB is also deferred to each individual Rice module. However,
org.kuali.rice.core.ojb.BaseOjbConfigurer can be extended by each module and included in it's list of Lifecycles to startup. This will load the OJB file for that module and merge it with OJB's global repository metadata.
Problems with Current Design
Ad-hoc Resource Loader semantics
Because each module is responsible for registering it's own Resource Loader with the GRL, this can lead hacks to try and influence the order in which Resource Loaders are configured. For example, note how most of the configurers use GlobalResourceLoader.addResourceLoaderFirst(...) to attempt to put their own RL at the front of the list. The is primarily done to ensure that the KSB's resource loader is at the end of the list since it is the piece that contacts the service registry to look for remote services.
Module Specification Order-Specific
A client of the Rice framework must know the proper order to specify the configurers in the modules section of RiceConfigurer. For example, KSBConfigurer must come before KEWConfigurer.
If they aren't specified in the proper order, developer will get obscure errors about missing services.
Must specify all Configurers
Even if there are not additional properties that need to be specified on a Configurer, it still has to be created and injected into the RiceConfigurer modules list. For example, the
KCBConfigurer doesn't typically require any additional properties. It would be nice to be able to omit the specification of this configurer in the cases where the default configuration is sufficient.
New Design Proposal
Based on Discussion between myself (Eric) and Poonam.
Refactor ModuleConfigurer to add the following methods:
ModuleConfigurer implementations will be refactored as follows:
- Will provide a reference to their Spring file via the
- Will no longer configure OJB internally (will need to be configured via a Spring bean)
- Will no longer register a
ResourceLoaderwith the GRL.
- Will continue to invoke any required lifecycle code in their
- Will continue to handle Config processing in loadConfig method.
RiceConfigurer will be refactored as follows:
- Will continue to delegate to ModuleConfigurers in order to establish Config.
- Will request name of all Spring Bean files from ModuleConfigurers.
- In it's start() method, will create a SpringContext using the Spring files from the modules and wrap in a ResourceLoader.
- Will create a ResourceLoader which attempts to locate services (and objects) using other Resource Loaders in the following order:
- Search rootResourceLoader injected by user into RiceConfigurer if it exists
- Search ResourceLoader containing Rice spring context.
- Search KEW PluginRegistry
- Search KSB RemotedServiceRegistry
- This ResourceLoader will also be in charge of starting the PluginRegistry and RemotedServiceRegistry when start() is invoked on it
- RiceConfigurer will register the resulting ResourceLoader with the GRL and invoke GRL.start()
Handling Dependencies between Modules
For things like KSBExporter where the KewSpringBeans.xml file is attempting to access a service from KSBServiceLocator, all of those cases will need to be identified and addressed. The simplest solution is to allow for the needed service to be optionally injected into the class.
So, for example, with the KSBExporter we can inject the enServiceInvoker server from KSBSpringBeans.xml. The KSBExporter will then just need to check for an injected service first before delegating to the KSBServiceLocator.
There may be more cases than KSBExporter where this will be a problem so we just need to identify all of those.
Other Potential Enhancements to Consider
- Remove the modules List from RiceConfigurer and replace with explicit settings for each of the ModuleConfigurers. This will make configuration a bit less verbose from the client application perspective. Will also eliminate the issue of the client application having to know the proper order in which to inject the modules (although the new single-spring context architecture may make that moot anyway).
- Add configuration options which turn on and off (or configure special) configurers for certain configuration options, for example:
- KEW Thin Client
- Full Embedded
- KEW Embedded
- KSB client