Resolver problema LockTimeoutException en Spring Web Flow
0. Índice de
contenidos.
1. Entorno
Este tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil Mac Book Pro 17″ (2,6 Ghz Intel Core i7, 8 GB DDR3)
- Sistema Operativo: Mac OS X Snow Leopard 10.6.4
- Maven 3.0.4
- Spring 3.0.5.RELEASE
- Spring Web Flow 2.3.1
2. Introducción
Un situación muy común que nos podemos encontrar a la hora de trabajar con Spring Web Flow y más cuando interactuamos con sistemas «lentos» como base de datos o llamadas a web services es que el timeout de llamada en un flujo está establecido a 30 segundos.
Lo que quiere decir que pasado este tiempo si el flujo no ha obtenido una respuesta nos lanzará una excepción que puede parecerse a esta:
ERROR [btpool0-3] BaseXMLFilter.doXmlFilter(157) – Exception in the filter chain
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.webflow.conversation.impl.LockTimeoutException: Unable to acquire conversation lock after 30 seconds
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:583)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093)
at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:154)
at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:260)
at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:366)
at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:493)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:359)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
Caused by: org.springframework.webflow.conversation.impl.LockTimeoutException: Unable to acquire conversation lock after 30 seconds
at org.springframework.webflow.conversation.impl.JdkConcurrentConversationLock.lock(JdkConcurrentConversationLock.java:44)
at org.springframework.webflow.conversation.impl.ContainedConversation.lock(ContainedConversation.java:69)
A priori la solución pasa por aumentar ese valor ajustándolo al más lento de nuestros procesos. Si nos ponemos a googlear nos damos cuenta de que no es tan sencillo como añadir un parámetro en un fichero de configuración con el nuevo valor, es un poco más elaborado.
En este tutorial vamos a ver los pasos que hay que dar para modificar este valor.
3. Pasos a seguir para solucionarlo
Lo primero es definir el siguiente bean dentro de nuestro fichero de configuración de Spring Web Flow.
<bean id="webFlowHelper" class="com.autentia.webflow.WebFlowHelper"> <constructor-arg index="0" ref="flowExecutor"> <property name="lockTimeoutSeconds" value="1000"> </property></constructor-arg> </bean>
Este bean al que podemos llamar «WebFlowHelper» recibe como argumento del constructor la referencia del FlowExecutor que tiene que estar previamente definido en nuestra configuración y establece la propiedad «lockTimeoutSeconds» con el valor que nosotros queramos, en mi caso, 1000 segundos. Recordad que este valor es en segundos.
Ahora basta con crear la clase especificada de la siguiente manera:
package com.autentia.webflow; import org.springframework.webflow.conversation.impl.SessionBindingConversationManager; import org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository; import org.springframework.webflow.executor.FlowExecutor; import org.springframework.webflow.executor.FlowExecutorImpl; public class WebFlowHelper { private FlowExecutor flowExecutor; public WebFlowHelper(FlowExecutor flowExecutor) { this.flowExecutor = flowExecutor; } public FlowExecutor getFlowExecutor() { return flowExecutor; } public SessionBindingConversationManager getConversationManager() { return ((SessionBindingConversationManager) ((DefaultFlowExecutionRepository) ((FlowExecutorImpl) flowExecutor).getExecutionRepository()).getConversationManager()); } public int getLockTimeoutSeconds() { return getConversationManager().getLockTimeoutSeconds(); } public void setLockTimeoutSeconds(int lockTimeoutSeconds) { getConversationManager().setLockTimeoutSeconds(lockTimeoutSeconds); } }
Cuando se levanta el contexto de este bean en el arranque servidor se establece el valor de la propiedad «lockTimeoutSeconds» en el atributo del mismo nombre de la clase ConversationManager que es la encargada de manejar las conversaciones entre el flujo y los servicios.
4. Conclusiones
Hecho esto, volvemos a arrancar el servidor y comprobaremos que ya no se produce la excepción a no ser que el servicio al que llamamos tarde en responder más de lo que hemos especificado en responder.
Espero que esto le sirva de ayuda a alguien.
Cualquier duda o sugerencia en la zona de comentarios.
Saludos.
Ok, Gracias por la entrada. He intentado usarla aunque a mí el atributo anidado dentro de me da un fallo:
cvc-complex-type.2.4.a: Invalid content was found starting with element ‘property’.
Aunque todo lo demás parece estar bien, si solo defino la property si el argumento constructor que ocurriría?.
Bueno es quizás hace mucho que se escribió este artículo para esperar una respuesta…
Gracias de todas formas,