This document describes how the Rice Service Architecture operates.
The Rice System consists of a stack of ResourceLoader objects that contain configuration information and expose service implementations (potentially from remote sources). Each module supplies its own Spring context containing it’s services. These Spring contexts are then wrapped by a ResourceLoader which is used to locate and load those services.
Rice is composed of a set of modules that provide distinct functionality and expose various services. Each module loads it’s own Spring context which contains numerous services. These Spring contexts are wrapped by a ResourceLoader class that provides access to those services. A ResourceLoader is similar to Spring's BeanFactory interface, since you acquire instances of services by name. Rice adds several additional concepts, including qualification of service names by namespaces. When the RiceConfigurer is instantiated, it constructs a GlobalResourceLoader which contains an ordered chain of ResourceLoader instances to load services from:
All application code should use the GlobalResourceLoader to obtain service instances. The getService(…) method iterates through each registered ResourceLoader to locate a service registered with the specified name. In it’s default configuration, the GlobalResourceLoader contacts the following resource loaders in the specified order:
Spring ResourceLoader – wraps the spring contexts for the various Rice modules
Plugin Registry – allows for services and classes from to be loaded from packaged plugins
Remote ResourceLoader – integrates with the KSB ServiceRegistry to locate and load remotely deployed services
As shown above, the last ResourceLoader on the list is the one registered by KSB to expose services available on the service bus. It’s important that this resource loader is consulted last because it gives priority to using locally deployed services over remote services (if the service is available both locally and remotely). This is meant to help maximize performance.
In addition to programmatically acquiring service references, you can also import Rice services into a Spring context with the help of the ResourceLoaderServiceFactoryBean:
<!-- import a Rice service from the ResourceLoader stack --> <bean id="aRiceService" class="org.kuali.rice.resourceloader.support.ResourceLoaderServiceFactoryBean"/>
This class uses the GlobalResourceLoader to locate a service named the same as the ID and produces a bean that proxies that service. The bean can thereafter be wired in Spring like any other bean.
Rice includes a Spring bean that extends the Spring auto-wire process (unlike the current version of Spring, the auto-wire process in the version of Spring that’s included with Rice cannot be extended). With this bean configured into your application, you can use the @RiceService annotation to identify Rice services to auto-wire.
Add this bean definition to the top of your Spring configuration file to configure the Spring extension:
<bean class="org.kuali.rice.core.util.GRLServiceInjectionPostProcessor"/>
Add the @RiceService annotation to any field or method, following the normal Spring rules for injection annotations. The annotation requires a name property that specifies the name of the service to inject. If the name requires a namespace other than the current context namespace, you must specify the namespace as a prefix (for example, “{KEW}actionListService”).
@RiceService(name="workflowDocumentService") protected WorkflowDocumentService workflowDocumentService;
In certain cases, it may be desirable to publish all beans in a particular Spring context to the Resource Loader stack. Fortunately, there is an easy way to accomplish this using the RiceSpringResourceLoaderConfigurer as shown below:
<!—- Publish all services from this Spring context to the GRL --> <bean class="org.kuali.rice.core.resourceloader.RiceSpringResourceLoaderConfigurer"/> <bean id="myService1" class="my.app.package.MyService1"/> <bean id="myService2" class="my.app.package.MyService2"/>
In the above example, both myService1 and myService2 would be added to a Resource Loader that would be put at the top of the Resource Loader stack. The names of these services would be “myService1” and “myService2” with no namespace. To load these services you would use the following call to the Global Resource Loader:
MyService1 myService1 = GlobalResourceLoader.getService(“myService1”);
The most common reason that one would want to override services in Kuali Rice is to customize the implementation of a particular service for the purposes of institutional customization.
A good example of this is the Kuali Identity Management (KIM) services. KIM is bundled with reference implementations that read identity (and other) data from the KIM database tables. In many cases an implementer will already have an existing identity management solution that they would like to integrate with. By overriding the service reference implementation with a custom one, it’s possible to integrate with other institutional services (such as LDAP or other services).
An alternative to using the RiceSpringResourceLoaderConfigurer to publish beans from a Spring context to the Rice Resource Loader framework is to inject a root Resource Loader into the RiceConfigurer.
You can create an implementation of ResourceLoader that returns a custom bean instead of the Rice bean, or you can use a built-in resource loader like the SpringBeanFactoryResourceLoader which wraps a Spring context in a ResourceLoader. Your configuration needs to inject this bean as the RootResourceLoader of the RiceConfigurer using the rootResourceLoader property, as shown below:
<!-- a Rice bean we want to override in our application --> <bean id="overriddenRiceBean" class="my.app.package.MyRiceServiceImpl"/> <!-- supplies services from this Spring context --> <bean id="appResourceLoader" class="org.kuali.rice.core.resourceloader.SpringBeanFactoryResourceLoader"/> <bean id="rice" class="org.kuali.rice.core.config.RiceConfigurer"> <property name="rootResourceLoader" ref="appResourceLoader"/> ... </bean>
Application Resource Loader and Circular Dependencies
Be careful when mixing registration of an application root resource loader and lookup of Rice services via the GlobalResourceLoader. If you are using an application resource loader to override a Rice bean, but one of your application beans requires that bean to be injected during startup, you may create a circular dependency. In this case, you have to make sure you are not unintentionally exposing application beans (which may not yet have been fully initialized by Spring) in the application resource loader, or you have to arrange for the GRL lookup to occur lazily, after Spring initialization has completed (either programmatically, or via some sort of proxy).
A Rice-enabled web application (including the Rice Standalone distribution) contains a RiceConfigurer (typically defined in a Spring XML file) that loads the Rice modules. You can override services from the various modules by injecting a list of additional spring files to load as in the following example:
<bean id="rice" class="org.kuali.rice.core.config.RiceConfigurer"> ... <property name="additionalSpringFiles" ref="appResourceLoader"> <list> <value>classpath:my/app/package/MyCustomSpringFile.xml</value> </list> </property> ... </bean>
You will need to ensure that any Spring XML files and necessary classes they reference are in the classpath of your application. If you are overriding things in the Rice standlone application itself, then you would need to place classes in the WEB-INF/classes directory of the war and any jars in the WEB-INF/lib directory.
It’s a standard behavior of Spring context loading that the last beans found in the context with a particular id will be the versions loaded during context initialization. The additionalSpringFiles property will put any Spring files specified at the end of the list loaded by the RiceConfigurer. So any beans defined in that file with the same id as beans in the internal Rice Spring XML files will effectively override the out-of-the-box version of those services.
When working with the packaged Rice standalone server, you won’t have access to the Spring XML file which configures the RiceConfigurer. In this case, you can specify additional spring files using a configuration parameter in your Rice configuration XML, as in the following example:
<param name=”rice.additionalSpringFiles” value=”classpath:my/app/package/MyCustomSpringFile.xml”/>