Access spring Request scope cache in Singelton bean called from fork-join/thread pool
Problem:
Spring enabled cache who’s scope is set to Request need to be accessed by a singleton bean which is not under the request scope.
Solution:
Spring enables you to create cache which keeps data for a request scope . For e.g.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.interceptor.SimpleCacheResolver; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; import org.springframework.web.context.WebApplicationContext; import java.util.ArrayList; import java.util.Collection; @Component @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public class RequestScopeCache extends SimpleCacheResolver { public RequestScopeCache() { SimpleCacheManager cacheManager = new SimpleCacheManager(); Collection caches = new ArrayList((Collection) new ConcurrentMapCache( "myCache" , true )); cacheManager.setCaches(caches); cacheManager.initializeCaches(); setCacheManager(cacheManager); } } |
And you can use this cache around any method which you would like to cache
1 2 3 4 | @Cacheable(value = "myCache" , cacheResolver = "requestScopeCache" ) public String getName(String id) { //logic to get name from id } |
Now it’s all good if you call this method from any controller which has a request context, i.e. this method is called from any other method of spring bean which is serving a web request.
But things become tricky if you need to call it from a thread pool or a fork join pool . Suppose you get a request and you need to spawn multiple threads to run concurrently to collect data to server the request.
These threads are running out side the context of your web request thread so any Thread Local value set on web request thread wont be available to these threads .
So if you end up calling the above method (annotated to use cache) from these pool threads you would get exceptions from spring like :
1 | Scope 'session' is not active for the current thread ; IllegalStateException: No thread -bound request found |
But there is an easy way to fix it :
- Get Request Attribute from the web request thread
1 | RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); |
2. Pass this attribute to your custom thread coming from pool or fork/join . Basically can be done by creating the runnable object with this attribute in its constructor
3. Set the request attribute before calling the method marked to use request scope cache
1 | RequestContextHolder.setRequestAttributes(attributes); |
This would set the attributes in the thread local of current thread which can be used to call the above method.
Synthetic request in Test cases
Now if you are testing your method from junit, there may be a chance that you don’t have request object at all .
So you can create one and use it as above to populate attributes to test
1 | RequestContextHolder.setRequestAttributes( new ServletRequestAttributes( new DummyRequest())); |
Published on Java Code Geeks with permission by Abhijeet Iyengar, partner at our JCG program. See the original article here: Access spring Request scope cache in Singelton bean called from fork-join/thread pool Opinions expressed by Java Code Geeks contributors are their own. |
was useful for me. thanks