Rice is composed of a set of modules that provide distinct functionality and expose various services.
Services in Rice are accessible by the ResourceLoader, which can be thought of as analogous to Spring's BeanFactory interface. (In fact, Rice modules themselves back ResourceLoaders with Spring bean factories.)
Services can be acquired by name. (Rice adds several additional concepts, including qualification of service names by namespaces.)
When the RiceConfigurer is instantiated, it constructs a GlobalResourceLoader that is composed of an initial RootResourceLoader (which may be provided by the application via the RiceConfigurer), as well as resource loaders supplied by each module:
The GlobalResourceLoader is the top-level entry point through which all application code should go to obtain services. The getService call will iterate through each registered ResourceLoader, looking for the service of the specified name. If the service is found, it is returned, but if it is not found, ultimately the call will reach the RemoteResourceLoader. The Root ResourceLoader is registered by the KSB module that exposes services that have been registered on the bus.
In addition to programmatically acquiring service references, you can also import Rice services into a Spring context with the help of the ResourceLoaderServiceFactoryBean:
This bean is bean-name-aware and will produce a bean of the same name obtained from Rice's resource loader stack. The bean can then be wired in Spring like any other bean.
As an alternative to the ResourceLoaderServiceFactoryBean style of importing Rice beans, you can use the @RiceService annotation in client application beans (in conjunction with the GRLServiceInjectionPostProcessor bean factory post processor) to auto-wire Rice beans into client application services. This is essentially a style of Spring autowiring
In later versions of the Spring framework, Spring has made autowiring support more flexible. However, in the version that Rice uses, autowiring is built-in and cannot be extended; which is why Rice has its own post-processor implementation.
First, define the GRLServiceInjectionPostProcessor in your application Spring context. Spring will run this post-processor during bean initialization and it will look for @RiceService fields or methods to inject.
<beans> <bean class="org.kuali.rice.core.util.GRLServiceInjectionPostProcessor"/> ... </beans>
Add the @RiceService annotation to any field or method (either, not both, for the same property) that you wish to have injected with a RiceService.
You must specify the name of the service you want to retrieve. To specify namespace, prefix with {NAMESPACE}. For example, {KEW}actionListService. If a namespace is not specified, the current context namespace will be used.
If you want to obtain the service from a specific, named ResourceLoader, you may specify the name of the ResourceLoader (refer to the particular ResourceLoader source to determine its name).
@RiceService(name="workflowDocumentService") protected WorkflowDocumentService workflowDocumentService;
Applications can install their own root ResourceLoader to override beans defined by Rice. To do so, inject a bean that implements the ResourceLoader interface into the RiceConfigurer rootResourceLoader property. For example:
<!-- 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.resourceloader.SpringBeanFactoryResourceLoader"/> <bean id="rice" class="org.kuali.rice.config.RiceConfigurer"> <property name="dataSource" ref="standaloneDataSource" /> <property name="transactionManager" ref="atomikosTransactionManager" /> <property name="userTransaction" ref="atomikosUserTransaction" /> <property name="rootConfig" ref="config" /> <property name="environment" value="${environment}" /> <property name="rootResourceLoader" ref="appResourceLoader"/> <property name="modules"> <list> <bean class="org.kuali.rice.ksb.messaging.config.KSBConfigurer"> <property name="serviceServletUrl" value="${serviceServletUrl}" /> <property name="authorizationService"> <bean class="edu.iu.uis.eden.authorization.WorkgroupAuthorizationServiceImpl"> <property name="workgroupName" value="WorkflowAdmin" /> </bean> </property> </bean> <bean class="org.kuali.workflow.config.KEWConfigurer"> <property name="clientProtocol" value="local" /> </bean> <bean class="org.kuali.notification.config.KENConfigurer" /> </list> </property> </bean>
Application ResourceLoader and Circular Dependencies
Be careful when mixing registration of an application root resourceloader and lookup of Rice services through the GlobalResourceLoader. If you are using an application resourceloader 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 will either have to make sure you are not unintentionally exposing application beans (which may not yet have been fully initialized by Spring) in the application resourceloader, or you will have to arrange for the GRL lookup to occur lazily, after Spring initialization has completed (either programmatically or through a proxy).
A Rice-enabled webapp (including the Rice Standalone distribution) contains a RiceConfigurer, typically defined in an xml Spring context file. This RiceConfigurer loads the Rice modules. Each module has its own ResourceLoader, which is typically backed by an XML Spring context file. Overriding and/or setting global beans and/or services (such as data sources and transaction managers) is done as described above. However, because in each module services are injected into each other, overriding module services involves overriding the respective module’s Spring context file.
There is no standard way to do this across modules. The KEW module has a flag for additional or replacement Spring context files, but others do not.To do so, one must override the module's Spring context file by placing a replacement in the appropriate location in the WEB-INF/classes directory, which will take precedence over the Rice-provided files in WEB-INF/lib.
The cleanest way to do this is take an unadulterated copy of the respective Rice Spring file, rename it, and then <import> it from your override Spring file. That way one does not have to copy and maintain the entire contents of the original Spring context file, e.g., to override a single KNS service without taking a copy of the entire KNS Spring context:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <!-- override of KNSSpringBeans.xml to override encryption service --> <beans> <!-- copy of original is imported --> <import resource="classpath:/KNSSpringBeans-0.9.2.1.xml"/> <!-- override encryption services --> <bean id="encryptionService" class="edu.my.school.service.impl.MyEncryptionServiceImpl" lazy-init="true"> <property name="cipherAlgorithm" value="${encryption.cipherAlg}"/> <property name="keyAlgorithm" value="${encryption.keyAlg}"/> <property name="key" value="${encryption.key}"/> <property name="enabled" value="${encryption.busEncryption}"/> </bean> </beans>