tags:

views:

388

answers:

3

I'm missing something....

I have a Grails webflow that looks like this:-

def childFlow = {
        start {
            action {
                def targets = []
                Target.list().each {target ->
                    targets.add(new TargetCommand(name: target.name, id: target.id))
                }
                log.debug "targets are $targets"
                [children: targets]
            }
            on('success').to('selectChild')
        }
        ...

TargetCommand is serializable. but I get this error:-

Caused by: java.io.NotSerializableException: com.nerderg.groupie.donate.Target

For some reason the "target" object that is inside the Target.list().each {} closure is getting put into the flow scope, and I can't figure out how to mark it as transient.

I have some code in a Service that has objects placed in the flow scope when I don't want them to too.

How do I stop local transient variables in closures being put in the flow scope?

A: 

The answer to my question is:

the flow object is a map that contains a reference to the "persistenceContext" which is a org.hibernate.impl.SessionImpl so the flow tries to store the entire session, even if the objects are not changed (for context I suppose)

this incorrect example from grails 1.1.x doc gives us a clue what to do:

processPurchaseOrder  {
     action {
         def a =  flow.address
         def p = flow.person
         def pd = flow.paymentDetails
         def cartItems = flow.cartItems
         flow.clear()

    def o = new Order(person:p, shippingAddress:a, paymentDetails:pd) 
    o.invoiceNumber = new Random().nextInt(9999999) cartItems.each { o.addToItems(it) }
    o.save() 
    [order:o] } 
    on("error").to "confirmPurchase" 
    on(Exception).to "confirmPurchase" 
    on("success").to "displayInvoice" 
}

The flow.clear() clears the entire flow map including the persistenceContext or the session, which then makes the whole flow fail due to lack of a session.

so the intermediate "solution" is to use the persistenceContext and in this case clear it. So this works:-

def childFlow = {
        start {
            action {
                sponsorService.updateTargetsFromTaggedContent()
                def targets = []

                Target.list().each {
                    targets.add(new TargetCommand(name: it.name, id: it.id))
                }

                flow.persistenceContext.clear()
                [children: targets]
            }
            on('success').to('selectChild')
            on(Exception).to 'finish'
        }

The obvious problem with this is that the session is cleared completely, instead of just keeping out things I don't want in the flow.

pmc
Thank you. I wasted an afternoon dumbfounded by this until I saw your answer. Very strange behaviour... not sure why the flow should be persisting the entire hibernate session by default, but clearing it works for me.
Martin Dow
A: 

Refining the above answer instead of clearing the persistenceContext we simply evict the instances as we finish with them, like so:

    Target.list().each {
        targets.add(new TargetCommand(name: it.name, id: it.id))
        flow.persistenceContext.evict(it)
    }

This is still a work-around for not being able to mark the closure variables as transient

pmc
A: 

for want of a better way, here is a generalised solution that removes any non Serializable objects from the persistenceContext of the flow. This could be a service method given the flow:-

def remove = []
flow.persistenceContext.getPersistenceContext().getEntitiesByKey().values().each { entity ->
    if(!entity instanceof Serializable){
        remove.add(entity)
    }
}
remove.each {flow.persistenceContext.evict(it)}
pmc
Which is all good and fine till you use a tag in the view that selects a domain object. I'm thinking either webflow isn't made for this or it's a braindead impl
pmc
fixed the tag issue by implementing a new tag that calls flowService.cleanUpSession(flow) - e.g. <nerderg:cleanupflow flow="${this}"/>
pmc
Note this seems to only work in the first state action.Other things that seem to work is to detach the domain object in question.If all elese fails implement serializable :-/
pmc