After a lot of googling and debugging I managed to produce the following code. It works in a simple application. I'll integrate it with the main application when I come back from holidays and then I'll update this question.
package org.glalejos
import org.springframework.context.ApplicationContext
import org.springframework.webflow.context.ExternalContext
import org.springframework.webflow.context.ExternalContextHolder
import org.springframework.webflow.context.servlet.ServletExternalContext
import org.springframework.webflow.execution.FlowExecution
import org.springframework.webflow.execution.FlowExecutionKey
import org.springframework.webflow.execution.repository.FlowExecutionRepository
class LoggingFilters {
def grailsApplication
String getFlowStateName(def grailsApplication, def servletContext, def request, def response) {
String stateName
if (grailsApplication && servletContext && request && request.queryString && response) {
try {
String strKey = null
String[] keys = request.queryString.split("&")
keys.each{ if (it.startsWith("execution=")) strKey = it.substring(10)}
if (strKey != null) {
ApplicationContext ctx = grailsApplication.mainContext
FlowExecutionRepository fer = ctx.getBean("flowExecutionRepository")
FlowExecutionKey fek = fer.parseFlowExecutionKey(strKey)
ExternalContext previousContext = ExternalContextHolder.getExternalContext()
try {
// You have to set an external context before invoking "fer.getFlowExecution()" or it'll throw a NPE
ExternalContextHolder.setExternalContext(new ServletExternalContext(servletContext, request, response));
FlowExecution fe = fer.getFlowExecution(fek)
stateName = fe.getActiveSession().getState().getId()
} finally {
ExternalContextHolder.setExternalContext(previousContext);
}
} else {
stateName = null
}
} catch(Exception e) {
stateName = null
}
} else {
stateName = null
}
return stateName
}
def filters = {
logData(controller:"*", action:"*") {
before = {
println("Incoming request. Current flow state name is: ${getFlowStateName(grailsApplication, servletContext, request, response)}")
}
after = {
println("Dispatched request. Current flow state name is: ${getFlowStateName(grailsApplication, servletContext, request, response)}")
}
}
}
}
EDIT: The code above is OK to determine the name of the current flow state at a given point in time, but it won't update the Mapped Diagnostic Context of the logging framework as the flow execution evolves. For this purpose it is necessary to implement a org.springframework.webflow.execution.FlowExecutionListener and register it in conf/spring/resources.groovy:
beans = {
myLoggingFlowExecutionListener(org.example.MyLoggingFlowExecutionListener)
}
You have to register this listener bean and the hibernateConversationListener bean in a executionListenerLoader bean, but, for some reason, Spring DSL doesn't work in this case (see EDIT2 below). So here is the resources.xml you can place in the same folder as resources.groovy in order to properly declare your resources:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="executionListenerLoader" class="org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader">
<constructor-arg>
<list>
<ref bean="hibernateConversationListener" />
<ref bean="myLoggingFlowExecutionListener" />
</list>
</constructor-arg>
</bean>
</beans>
Each FlowExecutionListener method receives a lot of context information that can be used for logging purposes (I'm ommiting the implementation of this class for clarity).
EDIT2: Failing to add the hibernateConversationListener bean to the executionListenerLoader results in Hibernate exceptions when manipulating domain objects during the lifecycle of the flow. However, Spring DSL doesn't work in this specific case, so I had to declare the required beans using XML format. See http://grails.1312388.n4.nabble.com/Registering-custom-flow-execution-listener-td2279764.html. I've updated the code above to the final, working, version.
FlowExecutionfrom theFlowExecutionRepository(I guess I can obtain it by using the flow key insidesession.webflowConversationContainer), but I can't figure out how to obtain those for any controller in a Grails filter. - Guillermo López Alejos