I was trying to implement a grails SearchService that indexes certain text and stores it in memory for faster lookup. In order to store this data, I was trying to use a private static property in the Service to store the data, but the property was randomly resetting values. After rereading documentation, I realized that this is likely because grails services are supposed to be stateless since the employee the singleton pattern. Still, not sure I understand how a static variable can vary. Does the JVM load separate copies of service classes per thread? Not sure I'm wrapping my head around what's happening.
Nonetheless, now that I know I can't rely on static variables to store application-wide data, what's the best approach to store and access data for use across the application, while keeping synchronization and avoiding races?
Caused by: java.lang.IllegalStateException: Method on class [TEXTSTORE] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly. at SearchService.buildIndex(SearchService.groovy:63) at SearchService$_indexAllDomains_closure2.doCall(SearchService.groovy:42) at SearchService.indexAllDomains(SearchService.groovy:41) at SearchService.$tt__rebuildIndex(SearchService.groovy:48) at SearchService.afterPropertiesSet(SearchService.groovy:35) ... 4 more
You seem to be a bit confused about services in Grails. There is no reason why a service (defaulting to singleton) can't have shared state. It's not uncommon for a service to populate some cached or indexed data when it is created so it can be used by multiple callers.
Most often this is done by implementing the org.springframework.beans.factory.InitializingBean
interface and making use of the afterPropertiesSet()
method which is called when the service (Spring bean) has been created in the application context and all dependencies have been resolved.
For example:
package com.example
import org.springframework.beans.factory.InitializingBean
class MyExampleService implements InitializingBean {
private List data
def otherService
void afterPropertiesSet() {
data = otherService.doSomethingToFetchData()
}
// .. other stuff
}
By hooking into the lifecycle of the bean you can be fairly sure that even in development (when your service reloads because you've changed some code) it will still have the data needed.