javagrailsgroovygrails-services

Include Service with Session Scope in Grails Controller?


Using Grails 2.4.5 I have the following service:

class CartService {

    static scope = 'session'
    static proxy = true

    def items = []

    def getItemCount() {
        items.size()
    }

}

I want to use this service in a controller:

class CartController {

    def cartService // unique per session

    def addItem = {
        cartService.items << new CartItem(product: Product.get(params.id))
    }

}

I tried version 0.3 of the Grails scoped-proxy plugin. But I get the following error:

Error creating bean with name 'cartService': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    Line | Method
->>  262 | run       in java.util.concurrent.FutureTask
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run       in java.lang.Thread
Caused by BeanCreationException: Error creating bean with name 'cartService': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
->>  262 | run       in java.util.concurrent.FutureTask
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run       in java.lang.Thread
Caused by IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
->>  262 | run       in java.util.concurrent.FutureTask
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run       in java.lang.Thread

How do I include Service with Session Scope in Grails Controller?


Solution

  • Looks like you might have found an issue or two (and also reported) with the plugin with Grails 2.4.x. If that is the case, you can refer to a blog post by the plugin author: Scoped Services & Proxies in Grails.

    There he shows how to do it without the plugin. I gave it a shot and it works in 2.4.5.

    resources.groovy:

    import org.springframework.aop.scope.ScopedProxyFactoryBean
    
    beans = {
        cartServiceProxy(ScopedProxyFactoryBean) {
            targetBeanName = 'cartService'
            proxyTargetClass = true
        }
    }
    

    Proxy is done manually, so you can remove static proxy

    class CartService {
        static scope = 'session'
        def items = []
    
        def getItemCount() {
            items.size()
        }
    }
    

    Controller then references the proxy

    class CartController {
        def cartServiceProxy 
        ... 
    }
    

    Note that one point of confusion (for me at least) was that the Controller Documentation for 2.4.x says the default scope is prototype but in the section describing Scoped Services it says that as of 2.3.x the default controller scope is singleton.

    Hope this helps.

    Update: To clarify a bit, the default controller scope is still prototype after Grails 2.3, but when new applications are generated by grails, configuration is generated that sets default controller scope to singleton (which is really what you should shoot for if possible like Burt says below).

    Generated config in Config.groovy

    // The default scope for controllers. May be prototype, session or singleton.
    // If unspecified, controllers are prototype scoped.
    grails.controllers.defaultScope = 'singleton'