Entradas con la etiqueta ‘jbpm’
-
No hay comentarios »
Pues nada, que creo que ya hemos solucionado el tema, o por lo menos eso dicen mis tests ;-). Y la solución que hemos tomado no la veo por ningún lado de hibernate.org, y en algunos casos puede ser muy útil: cuando hacemos uso de una librería que haga uso de hibernate y no queremos obligar al programador a conocer los entresijos de las sesiones y las transacciones, pero a la vez queramos hacer uso de la caché de primer nivel de forma eficiente. Perdonad lo pretencioso del título pero es que me ilusiono rápido, a ver si me hacéis caer de las nubes y me decís los problemas que le véis :-) (cuatro ojos ven más que dos) Veamos, el problema con jbpm es que usaba el lazy loading ampliamente a lo largo de su código. Al dar implementación al workflow de OCAS nos encontramos con el siguiente problema: se creaba un proceso en una sesión de jbpm (=sesión hibernate), pero además teníamos un método process.getVariables() en el que teníamos que abrir una nueva sesión jbpm, cargarnos de la base de datos el proceso (que ya teníamos en memoria), y entonces hacer el jbpmProcess.getVariables() (y el lazy loading funcionaba porque ahora el proceso SÍ estaba cargado en la misma sesión en la que iba a cargar las variables). Un poco de código:
Buff HORRIBLE! Y ¿porqué lo teníamos que realizar así? Porque realmente estabamos usando el patrón de hibernate session-per-request lo que pasa es que OCAS es una librería cuyas peticiones van a venir de otro código, que no sabemos exactamente a qué métodos va a llamar (en algún sitio puede que sólo llamen a startProcess, en otros puede llamar a los dos métodos presentados). En una aplicación web, el patrón session-per-request tiene sentido ya que conocemos que vamos a hacer en cada petición (traernos datos de la bd, modificarlos y salvarlos), pero nuestro problema es que no conocemos a priori que métodos iban a ser llamados, con lo cual debíamos contemplar cada llamada al método del wfService como una petición independiente.public class JBPMWorkflowService implements WorkflowService{ (...) public net.javahispano.ocas.workflow.Process startProcess(String processName, String usr) { JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession(); org.jbpm.graph.def.ProcessDefinition definition = jbpmSession .getGraphSession() .findLatestProcessDefinition(processName); ProcessInstance jbpmProcess = new ProcessInstance(definition); jbpmSession.getGraphSession().saveProcessInstance(jbpmProcess); jbpmSession.close(); JBPMProcess proceso = new JBPMProcess(jbpmProcess); return proceso; } }
public class JBPMProcess implements Process{ (...) public Map getVariables(){ JbpmSession sesion = wfService.getJbpmSession(); ProcessInstance process = sesion.getGraphSession() .loadProcessInstance(processInstance.getId()); Map result = process.getContextInstance().getVariables(); wfService.closeJbpmSession(sesion); return result; }
De repente surgió la idea: el patrón sesión por transacción de aplicación, pero un poco al revés. En hibernate dicen que usando los métodos connect y disconnect puedes hacer que una sesión dure varias transacciones; pues bien lo que necesitábamos es usar los métodos connect y disconnect entre las llamadas a los métodos del WF, con ello conseguiríamos:
- No abrir una nueva sesión en cada llamada a los métodos
- Aprovecharnos de la caché de primer nivel de hibernate: por ejemplo si alguien llamase al método getVariables dos veces seguidas, solamente en la primera habría un acceso a la base de datos
Claro está, que no hay nada gratis, para poder implementar la idea nos obligaba a idear algún sitio donde la sesión se abriese por primera vez y se cerrase de una vez por todas, así que “algo” sí hemos tenido que cambiar la interfaz: ahora aparece el concepto de ProcessTransaction (con sus métodos open(), commit() y rollback de toda la vida ;-)) Pero eso sí, para nada aparece el concepto de ProcessSession, no existe, las sesiones jbpm son transparentes al programador que use OCASOtro poco de código:
public class JBPMProcessTransaction implements ProcessTransaction{ private JbpmSessionFactory sesionFactory; private JbpmSession jbpmSession; private Session hibernateSession;Y ahora el método de antes cambia así:(..)
public void open() { if(jbpmSession.getCurrentJbpmSession() == null){ jbpmSession = sesionFactory.openJbpmSession(); }else{ jbpmSession = jbpmSession.getCurrentJbpmSession(); } jbpmSession.beginTransaction(); jbpmSession.getSession().disconnect(); }}
public Map getVariables(){ JbpmSession sesion = JbpmSession.getCurrentJbpmSession(); sesion.getSession().reconnect();Para que esto funcione, el usuario debe abrir y cerrar transacciones con el servicio de workflow:Map result = processInstance.getContextInstance().getVariables(); sesion.getSession().disconnect(); return result;}
ProcessTransaction transaction = new JBPMProcessTransaction(); transaction.open(); wfservice.deployProcessDefinition(processDefinition); net.javahispano.ocas.workflow.Process proceso = wfservice.startProcess("TestModel", "Test"); wfservice.stateEnded(proceso);El método getCurrentSession(), es un método que proporciona jbpm y que funciona como un HibernateUtil (ver primer post de la serie). En todas las llamadas a esos métodos se usa la misma sesión jbpm (que al desconectarse entre llamadas a métodos no ocupa una conexión del pool), aprovechándose de su caché, y cuando finalmente se llama a transaction.commitAndClose(), todo se guarda en la BD y se cierra la conexión.assertEquals(proceso.getVariable(JBPMProcessDefinition.AUTHOR), "Test"); assertEquals(proceso.getVariable(JBPMProcessDefinition.DESCRIPTION), "Test description"); transaction.commitAndClose();
En cuanto a hibernate, el pretencioso título de este post quiere decir, que he visto como patrón una sesión larga con varias transacciones asociadas, pero que lo que se trata es de una transacción con una sesión que se conecta y desconecta. No hemos inventado nada pero para este tipo de problemas, como el que se ha planteado, puede ser la solución genérica. Ahora mismo le estamos dando vueltas pero “intuimos” que si guardasemos la ProcessTransaction en un objeto HttpSession, se podría implementar con el mismo código el patrón “Long Session” de hibernate (lo de que una sesión permanece a través de varias transacciones, que en nuestro caso lo que queremos es que la sesión permanezca a través de varias peticiones, que pueden ser de una aplicación web o de escritorio). Ya os contaré (si aguantáis mis tostones ;-)) PD: ya sé que sigue siendo feo, tener esas llamadas a reconnect y disconnect en cada llamada a los métodos del workflow de OCAS, la idea es que en la próxima refactorización se usará AOP, pero ya llegará ese post.
-
Sobre LinkedLinked es el blog de Linking Paths, la empresa aventurera e innovadora formada por Aitor Garcia, Alberto Molpeceres y Roberto Salicio. En él hablamos de nuestros productos, ideas, y de compañías que nos sirven como guía y ejemplo. Si quieres conocernos un poco mejor puedes revisar lo que hemos escrito en los archivos.
-
Proyectos, ideas, etc.



