Full text
Trabajo Fin de Máster Máster Universitario en Ingeniería de Telecomunicación Aplicación empresarial para la gestión de procesos de consentimiento Autor: Francisco Javier Ros Raposo Tutora y publicadora: Isabel Roman Martinez Tutor: Jorge Calvillo Arbizu Dpto. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla,2024
II
Trabajo Fin de Máster Máster Universitario en Ingeniería de Telecomunicación Aplicación empresarial para la gestión de procesos de consentimiento Autor: Francisco Javier Ros Raposo Tutora y publicadora: Isabel Roman Martinez Tutor: Jorge Calvillo Arbizu Dpto. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2024 III
IV
Índice de contenidos Índice de contenidos V Índice de figuras IX Índice de tablas XI Índice de códigos XIII Resumen XVII Abstract XIX Glosario de términos XXI 1 Introducción 1 1.1 Motivación 1 1.1.1 Políticas 1 1.1.2 Consentimientos 2 1.1.3 Ámbito sanitario 2 1.1.4 La gestión de consentimientos como un proceso 2 1.2 Objetivos 3 1.3 Desarrollo 3 1.3.1 Antecedentes del proyecto 4 2 Estado del arte 5 2.1 Maven 5 2.1.1 Construcción de un proyecto Java 5 2.1.2 Gestión de dependencias 6 2.1.3 Inicialización de proyectos 6 2.1.4 Plugins 6 2.1.5 POM (Project Object Model) 7 2.2 Spring Boot 7 2.2.1 Spring 7 2.2.2 Funcionamiento 7 2.2.2.1 Inyección de dependencias 8 2.3 Thymeleaf 9 2.4 BPM 10 2.4.1 Objetivos y desempeño 10 2.4.1.1 Gestión de workflow 11 2.4.1.2 BAM 12 2.4.1.3 Ventajas 12 V
2.4.2 Implementación BPM 12 2.4.3 BPMS 13 2.4.3.1 jBPM 13 2.4.3.2 BonitaSoft 13 2.4.3.3 Activiti BPM Platform 13 2.4.4 BPMN 13 2.5 jBPM 14 2.5.1 Comunidad kie 14 2.5.2 Composición de la herramienta 15 2.5.2.1 Motor jBPM 15 2.5.2.2 Business Central 16 2.6 Aplicación Empresarial (BA) 16 2.6.1 Desarrollo utilizando jBPM 18 2.6.1.1 Generación y configuración 18 2.6.1.2 Ejecución 19 2.6.1.3 Gestión de activos de negocio 20 2.6.1.4 Work Item Handler 20 2.6.2 API jBPM 22 2.6.2.1 API REST 23 2.6.2.2 API de servicios 23 2.6.2.2.1 Servicio de despliegue 24 2.6.2.2.2 Servicio de definición 25 2.6.2.2.3 Servicio de procesos 26 2.6.2.2.4 Servicio de datos de ejecución 26 2.6.2.2.5 Servicio de tareas de usuario 27 2.6.2.2.6 Servicio de consultas a la BD 29 2.7 FHIR 31 2.7.1 Recursos 32 2.7.1.1 Questionnaire 32 2.7.1.2 Questionnaire Response 33 2.7.1.3 Consent 34 2.7.2 Hapi FHIR 36 3 Solución desarrollada 39 3.1 Caso de uso 39 3.1.1 Solución propuesta 39 3.1.1.1 Uso de la solución 41 3.2 Definición de procesos 42 3.2.1 Proceso de solicitud de consentimientos 43 3.2.1.1 Inicio 44 3.2.1.2 Tarea automática personalizada 45 3.2.1.3 Tarea humana 45 3.2.1.4 Subproceso 46 3.2.1.5 Fin 46 3.2.2 Proceso de revisión de consentimientos 46 3.2.2.1 Tarea humana 48 3.3 Generación de la aplicación empresarial 48 3.3.1 Importación de activos de negocio en Business Central 50 3.4 Desarrollo de la aplicación de negocios 50 VI
3.4.1 Estructura del proyecto 50 3.4.2 Inyección de dependencias 51 3.4.3 Seguridad 52 3.4.4 Controladores spring 54 3.4.5 Vista 55 3.5 Hapi FHIR 55 3.6 Api de Servicios del motor jBPM 56 3.6.1 Creación de Unidades de Despliegue 56 3.6.2 Interacción de Procesos 57 3.6.3 Interacción con tareas 57 3.6.4 Consultas 59 3.7 Antecedentes de desarrollo 65 4 Conclusiones 67 4.1 Elección de API 67 4.2 Forma de trabajar con el motor de procesos 67 4.3 Frontend 68 5 Líneas de mejora y continuación 69 5.1 Líneas de mejora 69 5.2 Líneas de continuación 70 Apéndice A Entorno de desarrollo 71 A.1 Java Development Kit (JDK) 71 A.2 Maven 72 A.3 Git 72 A.4 IDE 73 A.5 Docker 74 A.6 Postgresql 75 A.6.1 Instalación en un contenedor Docker 76 A.7 Servidor FHIR 77 Apéndice B Patrones de diseño 79 B.1 Arquitectura MVC 79 B.1.1 Ejemplo web 79 B.1.2 Beneficios e inconvenientes 79 B.2 Patrón Factory 80 B.3 Patrón Abstract Factory 80 B.4 Clases de modelo 80 B.4.1 POJO 80 B.4.2 JavaBean 81 B.4.3 Entidad 81 B.4.4 DTO (Data Transfer Object) 81 B.5 Patrón DAO 81 Apéndice C Implementaciones de consultas fallidas 83 C.1 Servicios de datos de ejecución y de procesos 83 C.2 Servicio de datos de ejecución 86 VII
C.3 Servicio de consultas 89 Bibliografía 95 VIII
Índice de figuras Figura1: Diagrama con los componentes que forman la suite jBPM [1] 15 Figura2: Diagrama de alto nivel en el que aparecen los proyectos que componen la aplicación empresarial creada con jBPM [1] 18 Figura3: Diagrama de la relación entre los distintos conceptos de jBPM, enfatizando en todo lo relacionado con órdenes de trabajo (Work Items) 22 Figura4: Diagrama del ciclo de vida de las tareas 29 Figura5: Diagrama UML del recurso FHIR Questionnaire junto con los componentes que lo forman [2] 33 Figura6: Diagrama UML del recurso FHIR Questionnaire Response junto con los componentes que lo forman [2] 34 Figura7: Diagrama UML del recurso FHIR Consent [2] 36 Figura8: Diagrama de componentes de la solución desarrollada para la gestión de consentimientos de pacientes 40 Figura9: Diagrama de secuencia que muestra las interacciones entre los usuarios y los distintos componentes de la aplicación empresarial 41 Figura10: Diagrama BPMN del proceso de solicitud de consentimientos 44 Figura11: Diagrama BPMN del proceso de revisión de consentimientos 47 Figura12: Estructura del código fuente de la aplicación empresarial 51 IX
XVI
Resumen La aplicación desarrollada en el proyecto es la primera parte del sistema completo que se propone como solución para el caso de uso de la gestión de consentimientos de pacientes en una organización sanitaria. En el caso de uso se busca que los consentimientos de los pacientes lleguen a convertirse en políticas de control de acceso que se hagan efectivas en las distintas aplicaciones de dicha organización. Este caso de uso está muy alineado con la situación actual en la que cada vez se manejan mayores cantidades de datos personales, y por consecuencia aparecen leyes y normativas que las empresas deben cumplir para garantizar la privacidad de las personas. Para llevar a cabo la implantación de estas normas, puede ser necesario emplear mecanismos para poder continuar con el funcionamiento habitual. En el caso de las organizaciones sanitarias, determinadas actividades precisan de acceso a datos que a causa de la normativa, requieren de un permiso especial, un consentimiento, que permita una excepción. La aplicación desarrollada en el proyecto es aquella que va a permitir la gestión de los consentimientos por parte de los facultativos y los pacientes, ofreciendo las capacidades de solicitud y revisión de los consentimientos. Se han utilizado las tecnologías Java, Spring, jBPM y Hapi FHIR para desarrollar la aplicación. XVII
XVIII
Abstract The application developed in the project is the first part of the complete system proposed as a solution for the use case of patient consent management in a healthcare organization. The use case seeks to convert patient consents into access control policies that become effective in the different applications of the organization. This use case is focused with the current situation in which increasing amounts of data are being handled, and as a consequence, laws and regulations that companies must comply with are appearing. In order to carry out the implementation of these regulations, it may be necessary to employ mechanisms to be able to continue with normal operations. In the case of healthcare organizations, certain activities require access to data that, due to regulations, require special permission, a consent, which allows an exception. The application developed in the project is the one that will allow the management of consents by physicians and patients, offering the capabilities of requesting and reviewing consents. Java, Spring, jBPM and Hapi FHIR technologies have been used to develop the application. XIX
XX
Glosario de términos Arquetipo maven Un arquetipo maven es una plantilla o patrón configurable que sirve como base para desarrollar proyectos software que utilizan distintas librerías y frameworks de programación que es necesario preparar. BA (Business Application) una aplicación empresarial es una colección de componentes que proporciona una funcionalidad empresarial. Un software que permite solventar una necesidad de negocio específica. Se puede utilizar internamente o externamente, y se puede integrar con otras aplicaciones empresariales [5]. BAM (Business Activity Monitoring) conjunto de procesos y tecnologías que permiten el acceso a indicadores significativos sobre el funcionamiento del negocio, en tiempo real, consiguiendo así poder tener información valiosa en determinadas situaciones, además de ofrecer la posibilidad de mejorar la eficacia y velocidad de las operaciones empresariales [6]. BP (Business Process) un proceso de negocio o empresarial es un conjunto de actividades, realizadas en un orden específico, a través de las cuales se pretende alcanzar una meta u objetivo dentro de una organización [7], [8]. BPM (Business Process Management) es una disciplina o paradigma de operación empresarial cuyo objetivo es mejorar el desempeño (eficiencia y eficacia) de los procesos de una organización, y optimizar los mismos de manera continua. Esto se hace a través de la gestión de los procesos de negocio (BP - Business Process), los cuales hay que diseñar, modelar, organizar, documentar, analizar y optimizar de forma continua. Por lo tanto, puede ser descrito como un paradigma para la mejora continua de procesos. Una organización que funciona bajo el paradigma BPM define toda su carga de trabajo en procesos, los cuales permiten alcanzar los objetivos de la organización [9]–[11]. BPMN (Business Process Model and Notation) es una notación estandarizada que permite el modelado de procesos de negocio, en un formato de flujo de trabajo (workflow). El principal objetivo de BPMN es proporcionar una notación estándar que sea fácilmente legible y entendible por parte de todos los involucrados e interesados del negocio (stakeholders) [12]. FHIR (Fast Healthcare Interoperability Resources) es un estándar desarrollado por HL7 (Health Level Seven) International, una organización de desarrollo de estándares (Standards Developing Organization, SDO) para el ámbito de la salud. La motivación que promovió su desarrollo fueron las limitaciones presentes en los estándares de interoperatividad clínicos existentes hasta ese momento (como HL7 v2, v3 o CDA) [13]. XXI
Framework es un esquema de trabajo prediseñado, que proporciona una serie de convenciones y librerías para facilitar el diseño y desarrollo de aplicaciones. Es decir, funciona como una plantilla en la que los desarrolladores se pueden apoyar para crear proyectos nuevos, a través de bases que fueron hechas con anterioridad [14]. Git sistema de control de versiones distribuido, gratuito y de código libre, el cual permite la gestión de proyectos de cualquier tamaño. HAPI FHIR Es una implementación de código libre del estándar FHIR en el lenguaje de programación Java, desarrollado por la organización Smile Digital Health, una empresa que se dedica a la creación de soluciones para el sector sanitario [15]. Hibernate es una de las implementaciones de las especificación JPA. Es la que utiliza por defecto Spring Data JPA [16]. IDE (Integrated Development Environment) aplicación que ofrece una serie de funcionalidades para el desarrollo de software. JDBC (Java Database Connectivity) es una API que permite la ejecución de operaciones sobre bases de datos con el lenguaje de programación Java, independientemente del sistema operativo donde se ejecute, o de la base de datos a la cual se accede, utilizando el dialecto SQL del modelo de base de datos que se utilice. JDK(Java Development Kit) software que provee una serie de herramientas de desarrollo para la creación de programas java. JPA (Java Persistent Api) es la especificación de una API para la persistencia de datos, definida para la plataforma Java EE. Lleva a cabo el mapeo objeto-relacional (ORM) para la plataforma Java, de forma que facilita el mapeo de atributos entre una base de datos relacional tradicional y el modelo de objetos de una aplicación, ya sea mediante archivos declarativos (XML) o anotaciones en los beans de las entidades que permiten establecer estas relaciones [16]. Javadoc herramienta que utiliza los comentarios de documentación de los códigos fuente de java, para generar la documentación del código en distintos formatos. KIE Es una comunidad que lidera el desarrollo de numerosos proyectos de código abierto, los cuales son soluciones para la automatización de procesos de negocios, uso de inteligencia artificial en la nube. Algunas herramientas que desarrollan son: jBPM, OptaPlanner, Drools y Kogito [17]. KJAR (Knowledge Jar) Es un artefacto definido en jBPM, orientado específicamente al almacenamiento de modelos reglas y procesos, considerados activos empresariales, y que está optimizado para tal contenido. Maven Apache Maven es una herramienta de gestión de proyectos de software. Está basada en el concepto de Modelo de Objeto de Proyecto (POM), permitiéndole gestionar cualquiera de las etapas de un proyecto software, desde su compilación, hasta su despliegue, incluyendo aspectos como los informes y la documentación de un proyecto, todo a partir de una información central [18]. OMG (Object Management Group) grupo que establece estándares para los sistemas de información, como BPMN. POJO (Plain Old Java Object) Es el nombre que se le da a una clase java que no tiene ni una sola dependencia, y que no implementa ningún framework, lo que le brinda una gran capacidad de reutilización, puesto que puede ser utilizada por cualquier clase java. Habitualmente se utiliza para definir entidades. PostgreSQL sistema gestor de bases de datos relacional, de código abierto. XXII
Spring Java Spring Framework es un marco de trabajo empresarial y de código abierto, para el desarrollo de todo tipo de aplicaciones y programas Java, Kotlin o Groovy. Es de los framework más populares de java [19]–[21]. Spring Boot Spring Boot es una herramienta que facilita el desarrollo de aplicaciones bajo el framework Spring, proporcionando mecanismos de dependencias prediseñados, y evitando tener que configurar los mecanismos spring necesarios para el proyecto en desarrollo [22]. Thymeleaf Thymeleaf es una biblioteca Java que implementa un motor de plantillas de XML/ XHTML/HTML5 (también extensible a otros formatos) que puede ser utilizado tanto en web como en otros entornos. Se acopla muy bien para trabajar en la capa vista del MVC de aplicaciones web, pero puede procesar cualquier archivo XML, incluso en entornos desconectados. Uno de los puntos más fuertes de esta tecnología es que las plantillas pueden ser renderizadas en local de la misma manera que se hace dentro del motor de plantillas. jBPM (Java Business Process Management) jBPM es un conjunto de herramientas para el desarrollo de soluciones BPM. Incluye un motor de flujo de trabajo, que permite la gestión de procesos de negocio y el control de los flujos de trabajo empresariales. Se utiliza habitualmente para crear BAs llevando a cabo la automatización de los flujo de trabajo de un negocio sin ninguna restricción. Es de código abierto escrito en Java [23], [24]. XXIII
XXIV
1 Introducción En este proyecto se va a llevar a cabo el desarrollo de una aplicación empresarial (Business Application, BA) destinada a la gestión de consentimientos otorgados por pacientes en una organización sanitaria. En este capítulo se va a llevar a cabo una introducción a lo que se va a hacer, con qué tecnologías y qué es lo que motiva la ejecución del proyecto. Todo lo que se comenta es posteriormente detallado en el resto de capítulos. En el Capítulo2 se va a realizar una revisión sobre diferentes conceptos teóricos y las diferentes tecnologías que se emplean en el desarrollo del proyecto. En el Capítulo3 se detalla en profundidad qué es lo que se ha implementado, detallando algunos problemas y las soluciones que se les han dado. En el Capítulo4 se van a exponer las conclusiones extraídas de la ejecución del proyecto, y en el Capítulo5 se van a proponer líneas de mejora y continuación del proyecto, que constituirán lo que queda por hacer de cara a lograr el objetivo final, recogiendo numerosas funcionalidades y elementos del sistema que no entran en el alcance de este proyecto por la cantidad de trabajo que suponen. Al final del documento, hay una serie de apéndices que pueden servir de ayuda para momentos determinados, como a la hora de entender algún diseño del código realizado mediante patrones de diseño, o para acondicionar un entorno de trabajo adecuado para trabajar en el desarrollo de la aplicación. 1.1 Motivación En esta sección se va a exponer todo aquello que detalla por qué tiene sentido realizar una aplicación empresarial para la gestión de consentimientos de pacientes. Se expone qué son las políticas que definen el funcionamiento de una organización, qué son los consentimientos y como aparecen, y por último como encaja todo esto en el ámbito sanitario, que es al que está destinada la aplicación empresarial desarrollada. 1.1.1 Políticas Es bastante habitual que las empresas y organizaciones cuenten con una serie de normas y directrices, encapsuladas en políticas, que guían las distintas decisiones que se toman, y que así definen cómo actúan los componentes de la organización, y en definitiva como funciona esta de manera global. 1
Capítulo 2 Estado del arte • «src/test/java»: directorio en el que se encuentran los ficheros java que se utilizan en la ejecución de test unitarios. No se incluyen en el binario final. • «src/test/resources»: directorio con recursos necesarios para la ejecución de los test unitarios. Los recursos no se incluyen en el binario final. • «target/classes»: directorio en el que se almacenan las clases java resultantes de la compilación de los ficheros java almacenados en «src/main/java». • «target/proyecto.jar»: binario producido por la compilación de todo el proyecto, que en esencia incluye el contenido de las carpetas «target/classes» y «src/main/resources». En el directorio «resources» se define un fichero de configuración application.properties , el cual utiliza Spring por defecto. Es posible definir ficheros de configuración alternativos, relacionados a un perfil application-perfil.properties , el cual será posible utilizar como alternativa al predeterminado al ejecutar la aplicación java con una opción: java -Dspring.profiles.active=perfil -jar main.jar Código2: Comando para arrancar un programa java, desarrollado en Spring, con un fichero de configuración alternativo 2.2.2.1 Inyección de dependencias Spring se centra en la simplificación y flexibilidad del desarrollo, esto lo consigue gracias a la inyección de dependencias y a la gestión del ciclo de vida de los objetos conocidos como «beans». Dentro de la aplicación, la gestión de los beans tiene lugar en el contexto en el que se instancian, y mediante la inyección de dependencias estos beans son accesibles desde donde son necesarios. Las anotaciones son el mecanismo que ofrece Spring para configurar los beans y utilizar la inyección de dependencias. Indican al framework la naturaleza que tiene una clase o un método, y cada una pertenece a alguno de los módulos disponibles en Spring, permitiendo configurar sus beans. Anotaciones Core: •@Autowired : se aplica a campos, métodos de “setters” y constructores. La anotación inyecta la dependencia del objeto implícitamente. Básicamente le indica a Spring que inyecte un objeto en otro cuando carga el contexto de su aplicación. Se suele utilizar para inyectar objetos de clases definidas como servicios. •@Configuration : se aplica a clases que se utilizan como configuración para el framework Spring, siendo una forma alternativa al uso de ficheros de configuración. Cada uno de sus métodos son beans, utilizados para crear instancias y configurar dependencias. •@Bean : se aplica en métodos de una clase de configuración. Los métodos indicados se encargarán de crear instancias, configurar dependencias y cargarlas al contexto de la aplicación. El método referido con esta anotación dará su nombre al identificador del bean. •@Value : se aplica en campos, parámetros de constructor y parámetros de método. Indica una expresión de valor predeterminado para el campo o parámetro para inicializar la propiedad. Se puede utilizar para inyectar valores desde un archivo de propiedades a un atributo de un bean. •@Override : se aplica en métodos, indicando que el comportamiento heredado es ignorado y totalmente reemplazado por el definido en el método. Anotaciones Estereotipo: •@Component : se aplica para marcar clases Java como un bean o componente para que el mecanismo de exploración de componentes de Spring pueda agregarla al contexto de la aplicación. •@Controller : se aplica para marcar clases Java como controladores Spring, ya sea para «Spring MVC» o «Spring WebFlux». 8
•@Service : se aplica para marcar clases Java que realizan algún servicio, como ejecutar lógica de negocios, realizar cálculos y llamar a API externas. Es como @Component pero destinada a ser utilizada en la capa de servicio. •@Repository : se aplica para marcar clases Java que realizan la función de repositorio u objeto de acceso a datos. Facilita la implementación del patrón repository, el cual se detalla en el ApéndiceB. Anotaciones Spring Boot: •@SpringBootApplication : se aplica a la función main de todo proyecto definido con Spring Boot. Se utiliza para que Spring realice un escaneo de componentes en todos los subpaquetes. Es una agrupación de las anotaciones @configuration , @EnableAutoConfiguration y @ComponentScan . Anotaciones REST: •@RequestMapping("/URI") : se aplica tanto a clases como a métodos. Se utiliza para asignar solicitudes web a clases y métodos manejadores específicos. Se le proporciona la URI para la que actúa de manejador, siendo a nivel de clase una uri base, y a nivel de método, una uri completa. Además se puede utilizar una operación HTTP. •@GetMapping : se aplica a métodos de clases controlador, para asignarles solicitudes HTTP GET de manera específica. •@PostMapping : se aplica a métodos de clases controlador, para asignarles solicitudes HTTP POST de manera específica. •@ResponseBody : se utiliza para indicar que el retorno de un método del controlador debe ser serializado directamente en el cuerpo de la respuesta HTTP, en lugar de ser utilizado para nombrar una vista a renderizar. •@RestController : se aplica a clases para indicar que cada uno de sus métodos devuelve una representación de un recurso en lugar de una vista. Anotaciones Security: •@EnableWebSecurity : se aplica a clases de configuración para tener definida la configuración Spring Security. 2.3 Thymeleaf Thymeleaf es un motor de plantillas Java, que trabaja en el servidor, para entornos autónomos y entornos web. Es una tecnología que nos va a permitir definir una plantilla y, conjuntamente con un modelo de datos, obtener un documento HTML [28]. El punto fuerte de Thymeleaf es que trabaja con plantillas naturales, lo que significa que pueden ser utilizadas en local, de la misma forma que será después utilizada con el motor de plantillas. Es integrable con muchos de los frameworks más utilizados, como por ejemplo Spring MVC y Java EE. Está basado en el uso de nuevas etiquetas, de nuevos atributos de HTML. Cuando se trabaja con Spring, los controladores que atienden peticiones REST devuelven como resultado contenido HTML. Esto se puede conseguir mediante el uso de las plantillas Thymeleaf. Para que esto ocurra el controlador recibe como parámetro un objeto de tipo Model, en el que se incluyen los datos que va a necesitar la plantilla para generar el contenido HTML. Finalmente el controlador indica la plantilla a utilizar y el framework se encarga de ejecutar el motor con el objeto Model, que contiene los objetos necesarios, para generar el contenido HTML que será respuesta a la petición que maneja el endpoint. 9
Capítulo 2 Estado del arte 2.4 BPM En primer lugar, antes de definir qué es BPM, conviene saber que un proceso empresarial o de negocio (Business Process, BP) es un conjunto de actividades agrupadas que permiten alcanzar un objetivo determinado, como la creación de un producto u ofrecimiento de un servicio [7], [8]. Los procesos en general, cuentan con una serie de entradas y salidas, de forma que las actividades que lo constituyen generan valor a partir de las entradas. Esas actividades pueden involucrar personas, organizaciones, aplicativos y eventos de negocio, que por tanto forman parte del proceso. Las empresas los utilizan para modelar sus objetivos de negocio, permitiéndoles definir como alcanzarlos. El paradigma de Gestión de Procesos de Negocio (Business Process Management, BPM) es una disciplina de trabajo centrada en la gestión de procesos empresariales, compuesta por una serie de metodologías que definen cómo se deben hacer las cosas, y que se apoyan en tecnologías (herramientas software) que facilitan su implementación [9]. Siguiendo la definición que ofrece el libro “BPM Common Body of Knowledge” [29], BPM puede definirse como un “enfoque disciplinado para identificar, diseñar, ejecutar, documentar, medir, monitorizar y controlar procesos de negocio, con el objetivo de lograr resultados alineados con las metas estratégicas de la organización” [10]. BPM proporciona en esencia un enfoque de gestión muy específico, centrado en la identificación, diseño, mantenimiento y evolución de procesos empresariales, integrados y eficientes, y que sirve como guía de gestión de procesos a la empresas. A pesar de estar muy ligado con la tecnología, no se limita únicamente a esta, puesto que facilita la integración de procesos con interacción humana, sin ser estrictamente necesaria la presencia de tecnología. Es un enfoque que fomenta la comunicación entre sistemas de la empresa, facilitando la comunicación entre analistas y desarrolladores gracias a la forma de diseño y trabajo que promueve. Mencionar que esto también es algo positivo para los clientes y usuarios de las organizaciones, puesto que permite que el servicio que se les ofrezca sea mejor. La metodología de trabajo BPM tiene su origen en los años 80, en el modelo de trabajo que utilizaba Toyota. A partir de ese método, con el tiempo fueron apareciendo normas y buenas prácticas de productividad y calidad, que acabaron consolidando BPM [9], [11]. 2.4.1 Objetivos y desempeño El objetivo final de BPM es mejorar la eficiencia y funcionamiento total de la empresa mediante la gestión (no solo optimización) sistemática de los procesos empresariales que constituyen el núcleo de su funcionamiento. Para llevar a cabo la gestión de procesos empresariales y conseguir optimizarlos, BPM define un ciclo de vida o de mejora continua, en el que se pueden agrupar distintas actividades de gestión en fases para modelar o diseñar, integrar, ejecutar, monitorizar y optimizar de forma continua los procesos empresariales de la empresa [30]–[32], [12]. En este ciclo de vida se distinguen las siguientes etapas: 1. Diseño: planteamiento inicial de los procesos de negocio de la empresa. En primer lugar se definen los objetivos que se quieren lograr. Una vez establecida la meta a la que se quiere llegar, se analizan los procesos de negocio existentes (que pueden no estar modelados), se planifican las modificaciones y los nuevos procesos de negocio que se tienen que implementar, para lo que se definen y diseñan, aclarando y organizando tareas, participantes y responsabilidades. 2. Modelado: creación de un modelo. 10
De manera individual, se crean y modifican los procesos según el diseño realizado en la fase anterior. Se describen en alguna notación. Se recomienda un lenguaje normalizado, como BPMN (Business Process Modeling and Notation), que permita que todo el mundo pueda visualizarlo y entenderlo de la misma manera, facilitando su optimización. Además, el modelo BPMN puede ser utilizado como base para su automatización y ejecución. 3. Ejecución: se integra y despliega lo que se ha diseñado y modelado. Los procesos empresariales modelados se despliegan y configuran en los motores de ejecución (parte de las BPMS (Business Process Management Suite o System)), en los que quedan disponibles para ser utilizados. Para la interpretación y ejecución de los procesos el motor necesita que estos sean definidos con la notación que estos manejen. Por ejemplo existen motores que manejan BPMN. Lo que ocurrirá será que cuando se ejecute un proceso, de manera manual o automática, se creará una instancia del proceso, que se ejecutará y monitorizará de manera individual. Los procesos se ejecutan como instancias de ejecución en un motor de procesos, de forma que es posible conocer el estado de ejecución de las instancias, lo que permite supervisar sus valores empleando alarmas para identificar problemas en tiempo real. 4. Monitorización: se supervisan y controlan resultados a través de KPIs (Key Performance Indicator). Una vez que los procesos se han ejecutado, los resultados que generan y los datos que contienen pasan a supervisarse y analizarse. Se definen una serie de indicadores (KPIs) que se miden y analizan para generar información sobre el funcionamiento de los procesos a partir del histórico de resultados. Esta información se utiliza para mejorar y optimizar los procesos. Se suele realizar una comparación entre los datos obtenidos y las especificaciones objetivo modeladas para ver cuánto se puede optimizar el proceso. 5. Optimización: remodelado de los procesos supervisados. Se realizan propuestas y modificaciones sobre el proceso, para optimizarlo, utilizando la información conseguida en la fase de monitorización, dándose paso de nuevo a la primera fase del ciclo de vida del proceso. BPM puede ser interpretada como un mecanismo de mejora continua de procesos, puesto que para lograr los objetivos que se persiguen lo que propone es un enfoque sistemático para diseñar, modelar, organizar, documentar y optimizar de forma continua [31]. Por tanto, dos de los aspectos fundamentales de BPM son: • La gestión del flujo de ejecución del proceso (workflow) • La monitorización de las actividades de negocio (Business Activity Monitoring, BAM) 2.4.1.1 Gestión de workflow Los procesos se definen con un workflow, una visión temporal de la secuencia de actividades implicadas en el proceso [33]. La gestión del workflow abarca las distintas tareas que funcionan dentro de un proceso. BPM por el contrario va más allá; tiene un enfoque más amplio y estructurado que el que ofrece la gestión de workflows, involucrando múltiples procesos. La gestión de workflows es utilizada dentro de BPM a la hora de trabajar con los procesos de manera individual [34], [35]. La diferencia en las áreas de trabajo que abarcan la gestión de workflow y la gestión de procesos (BPM) resulta en la existencia de herramientas muy variadas, unas para gestión de workflows y otras para 11
Capítulo 2 Estado del arte gestión de procesos empresariales, que además pueden ofrecer capacidades para la gestión de workflows [33]. 2.4.1.2 BAM La Monitorización de las actividades de negocio (Business Activity Management, BAM) es una actividad que permite el acceso en tiempo real a indicadores significativos sobre el comportamiento de la empresa, concretamente a los procesos empresariales. BAM además de registrar el éxito o fallo de los procesos empresariales, gestionar los problemas en tiempo real y monitorizar el comportamiento de los procesos, genera información muy valiosa para su posterior mejora y optimización, todo para finalmente mejorar la efectividad y velocidad de las operaciones empresariales. El cálculo de KPIs es el resultado más conocido que puede generar BAM. Entre los objetivos que persigue esta actividad, y que harán uso de los KPI, se encuentran: • Aumentar la productividad • Reducir costes • Evitar desperdicios o fallas de producción • Reducir burocracia interna • Generar más valor para su cliente 2.4.1.3 Ventajas Algunas de las ventajas que ofrece el uso de BPM en una empresa, son [30]: • Optimización y automatización de tareas y procesos empresariales • Fomento de la colaboración entre áreas y equipos de trabajo, así como facilidad de organización. • Fomento de la innovación general en la empresa, fruto de la monitorización y mejora continua que provoca BPM. • Aumento de competitividad en el mercado, ganando acceso a ciertas certificaciones que exigen eficiencia en la empresa. • Facilidad a la hora de tomar de decisiones • Interacción de calidad con el cliente, ya sea ofreciendo servicios o productos. • Transparencia y reducción de complejidad gracias a la optimización general. Todos estos beneficios que se obtienen de la gestión y mejora continua de procesos empresariales, se traducen para las empresas en eficiencia, competitividad y rentabilidad [30], [31]. 2.4.2 Implementación BPM Implementar esta disciplina de trabajo en cualquier empresa implica un gran cambio dentro de esta, siendo necesario un estudio y planificación previa, además de participación por parte de los empleados de la empresa, para que la empresa pueda funcionar mediante procesos empresariales. Según un pequeño artículo [9] se deben identificar 3 fases para que la implementación de BPM en una empresa sea correcta: • Selección de herramienta o suite BPM que permitirá la integración y uso de la metodología de trabajo en la empresa. • Ejecución de las distintas actividades del ciclo de vida de procesos BPM. • Formar al personal de la empresa. Los empleados y dirección de la empresa deben estar capacitados para convivir con la metodología de trabajo BPM. Esta fase se superpone con el resto. 12
2.4.3 BPMS Como se mencionaba antes, existe una serie de herramientas, conocidas como BPMS (Business Process Management Suite o System), que se van a encargar de dar soporte a las distintas etapas del ciclo de vida de los procesos, ofreciendo todo tipo de capacidades para la gestión de procesos empresariales, como por ejemplo, diseño y monitorización [12]. Es habitual que este tipo de herramientas faciliten la definición de métricas y KPIs para que los directivos puedan mejorar su toma de decisiones y contrastar el rendimiento que tienen sus estrategias empresariales. Algunos frameworks disponibles para trabajar con una arquitectura software BPM son [10]: • jBPM • BonitaSoft • Activiti • Camunda • Intalio, Orchestra, … • GPA PBM • Oracle Business Process Analysis Suite 10g 2.4.3.1 jBPM Es una suite BPM muy completa, que incluye numerosas herramientas y capacidades, y que lleva disponible bastante tiempo en el mercado. Está escrita en java y es de código libre. Su implementación está más orientada a nuevos proyectos, puesto que su adopción a proyectos existentes es bastante compleja [10]. 2.4.3.2 BonitaSoft BonitaSoft [36] es un set de herramientas que facilita la construcción de aplicaciones de negocio. Tiene una versión gratuita (Community Edition) y otra de pago (Subscription Edition). Entre las herramientas están disponibles un editor gráfico de procesos, un portal web para controlar las aplicaciones que utilizan procesos, y un motor de ejecución de procesos, así como herramientas de creación de formularios, y una serie de APIs para extender la funcionalidad. No está tan orientado a desarrollo como jBPM, puesto que ofrece más herramientas y capacidades para los gestores de procesos, que para la inclusión de procesos en el núcleo de aplicaciones [10]. 2.4.3.3 Activiti BPM Platform Activiti [37] es un motor de procesos ligero y robusto, escrito en java y de código libre, similar al del framework jBPM, que se puede incluir en cualquier proyecto java. Soporta la notación BPMN 2.0 [10]. Está muy orientado a desarrollo, y se utiliza a través de una API Java. Su inclusión en proyectos existentes es muy sencilla. Es fácil ampliar sus capacidades mediante el uso de librerías. Además de todo esto, incluye una pequeña aplicación web destinada a la gestión de procesos. Otra herramienta denominada Camunda [38] nació como un fork de Activiti [10]. 2.4.4 BPMN BPMN (Business Process Modeling and Notation) o Modelo y Notación de Procesos de Negocio, es una notación estándar, mantenida por el OMG (Object Management Group), la cual permite describir y representar los procesos empresariales, y que es crucial para poder implementar la metodología BPM en 13
Capítulo 2 Estado del arte una empresa, siendo clave para la definición de los procesos. En la actualidad, BPMN 2.0 es el estándar más utilizado para la definición de procesos [12]. BPMN está pensado para ser entendido, por cualquier empleado de una organización, y no solo por los profesionales de TI o procesos. Sin embargo, a diferencia de un diagrama de flujo simple, BPMN tiene recursos más completos y es capaz de representar procesos de una manera mucho más precisa [12]. 2.5 jBPM La documentación ofrecida por jBPM es muy extensa y autocontenida, desarrollando hasta el más mínimo detalle [1]. jBPM es una suite de herramientas que permiten la implantación del paradigma BPM (Business Process Management) en proyectos software. Está desarrollada por completo en java, y se trata de código libre, distribuido bajo la licencia Apache 2.0. Esta suite permite diseñar, ejecutar y monitorizar procesos y casos de negocio, a lo largo de todo su ciclo de vida [1]. Facilitando además la creación de aplicaciones empresariales (BA - Business Applications). Tal como dicta el paradigma BPM, un proceso de negocio o proceso empresarial, permite modelar los objetivos y metas que se persiguen en un negocio, describiendo las actividades que es necesario llevar a cabo para lograrlos, así como el orden que deben seguir. Entre los beneficios directos de la implantación de BPM (y por tanto el uso de procesos empresariales) en una empresa están, la optimización y la mejora de la monitorización de todo lo que ocurre en el negocio. jBPM se centra en la gestión y ejecución de procesos empresariales, con un nivel de detalle extra que permite que puedan ser ejecutados en un motor jBPM. De esta manera, jBPM, a través de sus herramientas y el concepto de proceso de negocio ejecutable, permite una comunicación muy precisa entre los expertos en negocios (lógica empresarial), y los desarrolladores de aplicaciones (funcionamiento); de forma que la lógica empresarial por la que se rigen para funcionar las organizaciones y BAs, queda simplificada y externalizada, en activos reutilizables; como casos, procesos, tablas de decisión y otros elementos [39]. 2.5.1 Comunidad kie En la actualidad la herramienta es mantenida por la comunidad KIE (Knowledge Is Everything), la cual lidera el desarrollo de numerosos proyectos de código abierto, los cuales constituyen soluciones para automatización de negocios y uso de inteligencia artificial en la nube. Además de jBPM, desarrollan las herramientas OptaPlanner, una herramienta que resuelve problemas con restricciones para la optimización, y Drools, un sistema de gestión de reglas de negocio [24], [17]. Conforme jBPM fue evolucionando, fue ofreciendo más flexibilidad de cara a la automatización de negocios, saliendo de lo que define BPM, y llegando en ciertas versiones a soportar su propio lenguaje de definición de procesos. En KIE se puede encontrar la implementación de distintos estándares para distintos casos de uso, los cuales son utilizados por las diferentes herramientas que desarrolla: • jBPM: ‣ business processes (BPMN2) ‣ case management (BPMN2 and CMMN) ‣ decision management (DMN) ‣ business rules (DRL) ‣ business optimisation (Solver) 14
• Optaplanner: ‣ Solver • Drools: ‣ DMN 2.5.2 Composición de la herramienta En la Figura1 se pueden visualizar todos los componentes que forman la suite jBPM, agrupados por funcionalidades, y que quedan disponibles a través del motor de procesos (motor jBPM o servidor kie), con el cual es posible comunicarse por las distintas API que soporta. Además, la suite jBPM cuenta con la herramienta web Business Central (BC), que ofrece una interfaz para poder gestionar por completo el motor de procesos, además de distintas utilidades, entre otras cosas para la gestión de activos de negocio. Figura1: Diagrama con los componentes que forman la suite jBPM [1] En esencia, jBPM está formado por el núcleo de ejecución jBPM, y alrededor de este se reúnen el resto de componentes, que hacen posible el soporte de los procesos de negocio a través de todo su ciclo de vida. Para profundizar en los distintos componentes, se recomienda consultar la documentación oficial de jBPM [1]. 2.5.2.1 Motor jBPM El núcleo de jBPM es un motor ligero de flujo de trabajo que permite la ejecución de procesos empresariales definidos siguiendo la última especificación, BPMN 2.0. Este motor está desarrollado por completo en java, lo que hace posible que pueda ser ejecutado en cualquier entorno java, ya sea como un componente más de una aplicación, o de forma totalmente autónoma como un servicio externo. Es el componente que debe estar presente de manera obligatoria para cualquier aplicación empresarial (BA) que quiera trabajar bajo el paradigma BPM utilizando jBPM. En el paradigma BPM, un proceso empresarial repetible es representado como un diagrama de flujo, en el que aparecen determinados elementos, denominados nodos, definidos según la especificación BPMN 2.0. Dicho esto, mediante el uso de jBPM, los expertos empresariales van a modelar diagramas de flujo de trabajo, y los desarrolladores van a desarrollar el código para cada unidad lógica del diagra- 15
Capítulo 2 Estado del arte ma, consiguiendo así procesos de negocio ejecutables, para que tanto los sistemas como los usuarios interactúen con ellos. La ejecución se lleva a cabo de forma que cuando se comienza un proceso, se crea una instancia de proceso, la cual contiene un contexto (una serie de datos y variables). Algunas de esas variables son de entrada, siendo necesarias para la ejecución de ciertas actividades del proceso, algunas son internas, siendo simplemente información que viaja entre actividades, y otras son de salida, suponiendo resultados de la ejecución del proceso. El motor funciona de forma que todas las actividades, las tareas, que son la unidad mínima e indivisible de acción, cuando se ejecutan pasan una orden de trabajo (Work Item (WI)) a una API gestora de órdenes de trabajo (Work Item Handler (WIH)). Cada tipo de tarea tiene asociado un gestor de órdenes de trabajo determinado en el que está implementada la lógica que va a ejecutarse a causa de la tarea. En jBPM vienen definidos una serie de tareas por defecto, sin embargo, es posible crear nuevos tipos de tareas, con una lógica diseñada desde cero, lo cual supone una gran flexibilidad a la hora de adoptar BPM mediante el framework jBPM. El motor jBPM utiliza la API KIE para interactuar con otros componentes software, de forma que los procesos de negocio se pueden ejecutar como servicios en el motor jBPM, y el software externo interactúa con ellos utilizando la API KIE. La API a utilizar podrá ser Java o REST a conveniencia. Esta API tiene distintos niveles de acceso y control que derivan en diferentes formas de hacer lo mismo, en las que cambian las condiciones. La existencia de una API REST hace posible que el componente pueda ser utilizado por una BA sin necesidad de ser desplegado junto a esta, y que se despliegue de manera autónoma o en la propia herramienta Business Central. 2.5.2.2 Business Central Business Central es una herramienta formada por un motor de procesos jBPM, dotado de una interfaz web que permite la gestión de los distintos componentes de la suite jBPM, creando una única solución genérica para la gestión de proyectos software desarrollados bajo el paradigma BPM durante todo su ciclo de vida. Entre otras cosas, esta herramienta permite el modelado de procesos y gestión de activos de negocio, la gestión de instancias de procesos, la monitorización de actividad empresarial, así como la gestión de tareas. También proporciona una GUI para poder ejecutar tareas humanas, gracias a formularios disponibles para ello, y un buzón para la gestión de las tareas disponibles para realizar. 2.6 Aplicación Empresarial (BA) Una aplicación empresarial (Business Application, BA) se puede definir como una solución, pensada para lograr determinadas metas y objetivos en una empresa, la cual reúne distintas tecnologías y servicios (que pueden ser independientes, con sus propios sistemas de versión y despliegue) que ofrecen funcionalidades tales como: • persistencia • comunicación • transacciones • procesos y reglas empresariales (paradigma BPM) • soluciones de planificación El desarrollo de una aplicación empresarial tal y como se acaba de definir se podrá conseguir a través de diferentes medios, pero en esta sección se va a hablar de cómo es una aplicación empresarial (BA) que utiliza el motor jBPM. 16
En las BA definidas según jBPM se incluye una serie de proyectos. Cada proyecto genera un artefacto identificable con GAV (Grupo, Artefacto y Versión), y disponibles en un repositorio maven, que podrá ser local o remoto. Los tipos de proyecto que se pueden encontrar son: • Activos de negocio (kjar): Proyecto que contiene archivos de información, (knowledge jar), que en este caso serán los activos de negocio tales como modelos de procesos, reglas, etc… • Modelo de datos: Entidades de información compartidas entre los activos de negocio y el servicio. Es un proyecto que debe estar disponible tanto para la aplicación como para los activos de negocio, puesto que tanto la aplicación como el motor de procesos pueden llegar a requerir de estos. •Servicio: Aplicación con distintas capacidades, y que puede incluir el despliegue de un motor de procesos jBPM (motor jBPM o kie-server) que va a permitir la ejecución de los procesos BPM en los que se apoya. Se trata de la lógica de la aplicación dado que incluye los gestores de órdenes (WIH) de las tareas personalizadas. La aplicación empresarial puede ser construida empleando distintos frameworks como SpringBoot, WildFly o Quarkus. En este proyecto se utiliza SpringBoot. Además es necesario configurar el servicio para definir: ‣ Despliegue de la aplicación, según donde se realice ‣ Perfiles de bases de datos ‣ Configuración del proyecto maven En una misma BA, puede haber más de un proyecto de cada tipo, de forma que se pueden tener varios conjuntos de activos de negocio, varios modelos de datos, y varios servicios, lo cual permite la modularidad de la aplicación. De manera opuesta, también es posible que alguno de los proyectos no sea necesario. Se puede ver en la Figura2 un diagrama a alto nivel de una BA ejemplo. 17
Capítulo 2 Estado del arte •jbpm-services-ejb-api : Extensión a la API de servicios para que se cumplan los requisitos de los EJB (Enterprise JavaBeans). •jbpm-services-ejb-impl : Envoltorios EJB superiores a la implementación de los servicios del núcleo. •jbpm-services-ejb-timer : Un servicio planificador basado en el servicio de temporizador de EJB para soportar operaciones basadas en tiempo. •jbpm-services-ejb-client : Implementación de un cliente EJB remoto, que actualmente es soportado solo por Red Hat JBoss EAP. Además de los servicios expuestos en los siguientes apartados existen otros dedicados a la creación de clústeres y migración. Todos los servicios expuestos en el módulo jbpm-services-api pueden ser inyectados como dependencias en Spring Boot, a través de la anotación @Autowired . Servicio de despliegue El servicio de despliegue del módulo jbpm-services-api permite desplegar unidades de despliegue (contenedores) en un motor de procesos, de forma que en los contenedores es donde se llevará a cabo la ejecución de los distintos activos de un proyecto KJAR. La unidad de despliegue representa un proyecto KJAR, y por tanto contiene todos los activos de negocio de ese proyecto, tales como definiciones de procesos, reglas, formularios y modelos de datos. Cada unidad de despliegue se identifica por un ID, llamado «deploymentUnitId», que a menudo es el «groupId:artifactId:version» (GAV) del proyecto KJAR al que representa. Para poner en marcha un proyecto KJAR en un motor de procesos, es necesario desplegarlo, de forma que se crea una unidad de despliegue o contenedor. En esa unidad de despliegue es donde se van a generar todas las instancias de procesos que se vayan generando. La interfaz del servicio que ofrece este servicio [3]: public interface DeploymentService { void deploy(DeploymentUnit unit); void undeploy(DeploymentUnit unit); RuntimeManager getRuntimeManager(String deploymentUnitId); DeployedUnit getDeployedUnit(String deploymentUnitId); Collection<DeployedUnit> getDeployedUnits(); void activate(String deploymentId); void deactivate(String deploymentId); boolean isDeployed(String deploymentUnitId); } Código8: Interfaz del servicio «DeploymentService» [3] Un código de ejemplo para la creación de una unidad de despliegue: 24
// Creación de la unidad de despliegue a partir del GAV del KJAR DeploymentUnit deploymentUnit = new KModuleDeploymentUnit(GROUP_ID, ARTIFACT_ID, VERSION); // Obtención del ID de la unidad de despliegue String deploymentId = deploymentUnit.getIdentifier(); // Despliegue de la unidad de despliegue deploymentService.deploy(deploymentUnit); // Obtención de la unidad desplegada DeployedUnit deployed = deploymentService.getDeployedUnit(deploymentId); // Obtención del gestor de ejecución de la unidad desplegada RuntimeManager manager = deployed.getRuntimeManager(); Código9: Fragmento de código para la creación de una unidad de despliegue mediante el uso del servicio «DesploymentService» Servicio de definición El servicio de definición del módulo jbpm-services-api permite la obtención de información de las definiciones de procesos de la unidad de despliegue indicada. La interfaz del servicio: public interface DefinitionService { ProcessDefinition buildProcessDefinition(String deploymentId, String bpmn2Content, ClassLoader classLoader, boolean cache) throws IllegalArgumentException; ProcessDefinition getProcessDefinition(String deploymentId, String processId); Collection<String> getReusableSubProcesses(String deploymentId, String processId); Map<String, String> getProcessVariables(String deploymentId, String processId); Map<String, String> getServiceTasks(String deploymentId, String processId); Map<String, Collection<String>> getAssociatedEntities(String deploymentId, String processId); Collection<UserTaskDefinition> getTasksDefinitions(String deploymentId, String processId); Map<String, String> getTaskInputMappings(String deploymentId, String processId, String taskName); Map<String, String> getTaskOutputMappings(String deploymentId, String processId, String taskName); } Código10: Interfaz del servicio «DefinitionService» 25
Capítulo 2 Estado del arte Servicio de procesos El servicio de procesos del módulo jbpm-services-api permite ejecutar los procesos definidos en la unidad de despliegue que se indique (que debe estar desplegada), interactuando con el motor jBPM, ofreciendo las siguientes capacidades: • Empezar una nueva instancia de proceso • Ejecutar un proceso como una transacción única • Trabajar con instancias de procesos existentes, para mandar señales, obtener y establecer variables. • Trabajar con ordenes de trabajo (work items) • Ejecución de comandos en la sesión KIE Sin embargo, para operaciones de lectura sobre las instancias de procesos que existen, se debe utilizar el servicio de datos de ejecución «RuntimeDataService». La interfaz del servicio está disponible en la documentación [3]. Un pequeño código de ejemplo para desplegar una unidad de despliegue, y poder así instanciar un proceso: KModuleDeploymentUnit deploymentUnit = new KModuleDeploymentUnit( GROUP_ID, ARTIFACT_ID, VERSION); deploymentService.deploy(deploymentUnit); long processInstanceId = processService.startProcess( deploymentUnit.getIdentifier(), "processID"); ProcessInstance pi = processService.getProcessInstance(processInstanceId); Código11: Fragmento de código para la creación de una unidad de despliegue y posterior instanciación de un proceso, utilizando el proceso «processService» Servicio de datos de ejecución El servicio de datos de ejecución del módulo jbpm-services-api permite obtener todo tipo de información relacionada con la ejecución, como datos sobre instancias de procesos o instancias de nodos ejecutados. Los métodos de este servicio trabajan con dos elementos que definen las consultas de datos. El obje- to QueryContext permite aplicar un pequeño procesado al conjunto de resultados que devuelve la consulta, permitiendo ordenarlos, limitar el tamaño de la respuesta, y obtener elementos a partir de un offset determinado, lo que puede ser útil para dividir la obtención de datos en distintas consultas. Por otro lado, el objeto QueryFilter hereda de la clase QueryContext , por lo que además de lo que su clase padre permite, implementa la posibilidad de filtrar los elementos del resultado de la consulta mediante atributos. Esto da la posibilidad de no tener que hacer un post filtrado de la consulta en un código a parte de la consulta. La interfaz del servicio está disponible en la documentación javadoc oficial [3]. Algunas peticiones de ejemplo: 26
// Obtención de todas las definiciones de procesos Collection definitions = runtimeDataService.getProcesses( new QueryContext()); // Obtención de todas las instancias de procesos activas Collection<processinstancedesc> instances = runtimeDataService.getProcessInstances(new QueryContext()); // Obtención de todos los nodos activos de una instancia de proceso sin llevar a cabo gestión de los resultados (Aunque no se utilice QueryContext, es obligatorio) Collection<nodeinstancedesc> instances = runtimeDataService.getProcessInstanceHistoryActive( processInstanceId, new QueryContext()); // Obtención de todas las tareas asignadas al usuario "john" sin llevar a cabo gestión de los resultados (Aunque no se utilice QueryFilter, es obligatorio) List<tasksummary> taskSummaries = runtimeDataService.getTasksAssignedAsPotentialOwner( "john", new QueryFilter()); Código12: Fragmento de código con la ejecución de distintas consultas utilizando el servicio «runtimeDataService» Servicio de tareas de usuario El servicio de tareas de usuario del módulo jbpm-services-api permite gestionar las tareas de usuario en cualquier etapa de su ciclo de vida, ofreciendo así capacidades como: • modificación de propiedades • acceso a variables de tareas • acceso a comentarios de tareas • acceso a anexos de tareas • ejecutar comandos relacionados con tareas que modificarán su estado El servicio trabaja sobre una tarea concreta, pero para realizar peticiones sobre tareas existentes, a un nivel mayor, es necesario utilizar el servicio de datos de ejecución «RuntimeDataService». La interfaz del servicio está disponible en la documentación [3]. El código de ejemplo que hay a continuación muestra cuál sería el flujo de trabajo para arrancar una instancia de proceso e interactuar con sus tareas: 27
Capítulo 2 Estado del arte // Se crea una instancia del proceso con identificador "org.jbpm.writedocument" long processInstanceId = processService.startProcess( deployUnit.getIdentifier(), "org.jbpm.writedocument"); // Se obtiene una lista de tareas disponibles en esa instancia de proceso List<Long> taskIds = runtimeDataService.getTasksByProcessInstanceId( processInstanceId); // Se obtiene el primer identificador de tarea de la lista Long taskId = taskIds.get(0); // El usuario "john" empieza la tarea obtenida userTaskService.start(taskId, "john"); // Se obtiene la entidad que describe la tarea a partir de su identificador. Contiene todos sus atributos y variables UserTaskInstanceDesc task = runtimeDataService.getTaskById(taskId); Map<String, Object> results = new HashMap<String, Object>(); results.put("Result", "some document data"); // Se completa la tarea obtenida con el usuario, y se inyectan los resultados userTaskService.complete(taskId, "john", results); Código13: Fragmento de código en el que se hace uso del servicio «userTaskService» para empezar una tarea Para poder entender cómo funciona jBPM y cómo están implementadas las tareas es recomendable visualizar la Figura4 que muestra el ciclo de vida de las tareas, con todos los estados en los que pueden encontrarse. 28
Figura4: Diagrama del ciclo de vida de las tareas Servicio de consultas a la BD El servicio de consultas a la BD del módulo jbpm-services-api permite ejecutar consultas personalizadas, gestionadas por la librería, de manera directa a la base de datos que utiliza el motor de procesos, en la que se almacenan, entre otras cosas, todas las instancias de procesos y tareas, junto con sus atributos y variables. Para poder lanzar consultas es necesario generar la consulta, registrarla, y después llevarla a acabo [4], [1], [3]. Algunas de las tablas existentes que hay en el modelo de datos de jBPM: • Instancias de proceso: processinstancelog • Variables de Instancias de proceso: VariableInstanceLog • Variables de Tareas: TaskVariableImpl Es posible leer la BD para la que se configure Spring Boot para poder visualizar cómo está diseñado el modelo de datos, sin embargo, en la documentación no aparece detallado el significado de cada una de las tablas del modelo de datos. Es un servicio que da mucha flexibilidad para obtener información que controla el motor de procesos, y que por tanto se podría utilizar para monitorizar variables y elementos del motor de procesos, lo que es muy útil a la hora de recolectar KPIs en las fases de análisis, algo muy útil para la monitorización de la aplicación empresarial, sobre todo cuando esta no se utiliza de la mano de Business-Central. Ejemplo de código en el que se consultan todas las Instancias de Proceso existentes [4]: 29
Capítulo 2 Estado del arte // Definición de una query que se ha denominado "getAllProcessInstances", y que obtiene todos los elementos de la tabla de instancias de procesos "processinstancelog". Esta consulta se define para ser utilizada en la fuente de datos "java:jboss/datasources/ExampleDS" SqlQueryDefinition query = new SqlQueryDefinition( "getAllProcessInstances", "java:jboss/datasources/ExampleDS"); query.setExpression("select * from processinstancelog"); // Registro de la query queryService.registerQuery(query); // Ejecución de la query anteriormente registrada con id "getAllProcessInstances" Collection<ProcessInstanceDesc> instances = queryService.query( "getAllProcessInstances", ProcessInstanceQueryMapper.get(), new QueryContext()); // Utilizando un query context configurado para que la consulta devuelva únicamente 100 elementos, que empiecen por el primero (offset 0), y que sean ordenados de manera ascendente utilizando la variable "start_date" QueryContext ctx = new QueryContext(0, 100, "start_date", true); Collection<ProcessInstanceDesc> instances = queryService.query( "getAllProcessInstances", ProcessInstanceQueryMapper.get(), ctx); // Filtrando por un parámetro Collection<ProcessInstanceDesc> instances = queryService.query( "getAllProcessInstances", ProcessInstanceQueryMapper.get(), new QueryContext(), QueryParam.likeTo(COLUMN_PROCESSID, true, "org.jbpm%")); // Filtrando por múltiples parámetros (AND), cada condición definida en un objeto "QueryParam", el primero indica que la columna debe tener un valor "org.jbpm%", teniéndose en cuenta mayúsculas y minúsculas, y el segundo que la columna a la que se refiere debe tener un valor igual a alguno de los propuestos (1 o 3) Collection<ProcessInstanceDesc> instances = queryService.query( "getAllProcessInstances", ProcessInstanceQueryMapper.get(), new QueryContext(), QueryParam.likeTo(COLUMN_PROCESSID, true, "org.jbpm%"), QueryParam.equalsTo(COLUMN_STATUS, 1, 3)); Código14: Fragmento de código en el que se utiliza el servicio «queryService» para consultar todas las instancias de proceso existentes Ejemplo de código que realiza una consulta para obtener las tareas que tienen una serie de variables [4]: 30
SqlQueryDefinition query = new SqlQueryDefinition( "getAllProcessInstancesWithVariables", "java:jboss/datasources/ExampleDS"); query.setExpression( "select pil.*, v.variableId, v.value " + "from ProcessInstanceLog pil " + "INNER JOIN (select vil.processInstanceId,vil.variableId, MAX(vil.ID) maxvilid FROM VariableInstanceLog vil"+ "GROUP BY vil.processInstanceId, vil.variableId ORDER BY vil.processInstanceId) x" + "ON (v.variableId = x.variableId AND v.id = x.maxvilid )" + "INNER JOIN VariableInstanceLog v " + "ON (v.processInstanceId = pil.processInstanceId)"); queryService.registerQuery(query); List<ProcessInstanceWithVarsDesc> processInstanceLogs = queryService.query( "getAllProcessInstancesWithVariables", ProcessInstanceWithVarsQueryMapper.get(), new QueryContext(), QueryParam.equalsTo(COLUMN_VAR_NAME, "approval_document")); Código15: Fragmento de código en el que se realiza una consulta para obtener las tareas que tienen una serie de variables [4] Ejemplo de código que realiza una consulta para obtener las tareas que tienen un valor determinado en una variable [4]: // Definición de una query que tiene como id "getAllTaskInputInstancesWithVariables" query = new SqlQueryDefinition( "getAllTaskInputInstancesWithVariables", "java:jboss/datasources/ExampleDS"); query.setExpression( "select ti.*, tv.name tvname, tv.value tvvalue "+ "from AuditTaskImpl ti " + "inner join (select tv.taskId, tv.name, tv.value from TaskVariableImpl tv where tv.type = 0) tv"+ "on (tv.taskId = ti.taskId)"); queryService.registerQuery(query); List<UserTaskInstanceWithVarsDesc> taskInstanceLogs = queryService.query( "getAllTaskInputInstancesWithVariables", UserTaskInstanceWithVarsQueryMapper.get(), new QueryContext(), QueryParam.equalsTo(COLUMN_TASK_VAR_NAME, "Comment"), QueryParam.equalsTo(COLUMN_TASK_VAR_VALUE, "Write a Document")); Código16: Fragmento de código en el que se realiza una consulta para obtener las tareas que tienen un valor determinado en una variable [4] 2.7 FHIR FHIR (Fast Health Interoperability Resources) es un estándar pensado para conseguir interoperatividad de sistemas sanitarios, definido por la organización HL7 (Health Level Seven International). 31
Capítulo 2 Estado del arte Este estándar define modelos de datos para la representación de entidades, de forma que todas las aplicaciones que lo implementen puedan llevar a cabo intercambio de datos sin ningún tipo de conflicto ni interpretación errónea del significado de los datos. Está diseñado para ser totalmente flexible y ampliable, basado en unos componentes modulares llamados recursos [2]. HL7 fue fundada en 1987 como una organización de normalización sin ánimo de lucro, acreditada por ANSI, que se dedica a proporcionar un marco integral y normas relacionadas para el intercambio, la integración, la puesta en común y la recuperación de información sanitaria electrónica que respalde la práctica clínica y la gestión, prestación y evaluación de servicios sanitarios. HL7 cuenta con el apoyo de más de 1.600 miembros de más de 50 países, incluidos más de 500 miembros corporativos que representan a proveedores de atención sanitaria, partes interesadas gubernamentales, pagadores, empresas farmacéuticas, vendedores/proveedores y empresas de consultoría [41]. Su nombre hace referencia a que trabaja en la séptima capa del modelo OSI, la capa de aplicación, en la cual entran en juego la definición de los modelos de datos. El estándar está en constante desarrollo y cada cierto tiempo cuenta con una nueva versión, en la que aparecen nuevos recursos y se modifican algunos existentes. Las versiones actualmente más recientes son la R4 y la R5. La razón por la que se impulsó el desarrollo del estándar FHIR fue la existencia de limitaciones en los estándares de interoperatividad clínicos existentes, tales como HL7 v2, v3 o CDA [13]. Para utilizar los servidores FHIR se puede utilizar la API REST con la que estos cuentan, la cual admite las siguientes operaciones: • Creación: POST https://fhirserver/{resourceType} • Lectura: GET https://fhirserver/{resourceType}/{id} • Actualización: PUT https://fhirserver/{resourceType}/{id} • Eliminación: DELETE https://fhirserver/{resourceType}/{id} • Búsqueda: GET https://fhirserver/{resourceType}?searh parameters • Historial: GET https://fhirserver/{resourceType}/{id}/_history • Transacción: POST (una transacción) https://fhirserver/ • Operación: GET https://fhirserver/{resourceType}/{id}/${opname} Estas acciones se pueden llevar a cabo desde cualquier cliente REST, un ejemplo puede ser mediante el uso de la herramienta «curl»: curl -X POST -H 'Content-Type: application/json' -d @req.json http://hapi.fhir.org/ baseR5/ Código17: Comando para ejecutar una petición POST a un endpoint REST 2.7.1 Recursos Tal y como se comentaba antes, el estándar FHIR está basado en recursos modulares que permiten definir el modelo de datos. En este proyecto se van a utilizar los recursos Questionnaire, Questionnaire Response y Consent, definidos en la versión R5 del estándar. 2.7.1.1 Questionnaire El recurso Questionnaire está formado, entre otras cosas, por los siguientes elementos [42], [2]: •resourceType: campo de tipo String que indica el tipo de recurso FHIR. En este caso el valor será Questionnaire. •identifier: campo que contiene el identificador del recurso en el servidor FHIR en el que se encuentra. 32
•status: campo de tipo Enumerations.PublicationStatus que contiene el estado de publicación en el que se encuentra el recurso. •tittle: campo de tipo String que describe brevemente al recurso. •item: este campo es una lista de componentes de cuestionario, de tipo Questionnaire.QuestionnaireI- temComponent que son las partes que forman el cuestionario. El componente Questionnaire.QuestionnaireItemComponent del recurso Questionnaire representa un elemento presente en el cuestionario. Está formado por: •linkId: campo que contiene un identificador único dentro del cuestionario, que permite establecer un vínculo con el elemento equivalente en un recurso QuestionnaireResponse. •text: campo que contiene el nombre de una sección, el texto de una pregunta o el contenido del texto de un elemento de la pantalla, según el tipo de componente del que se trate. •required: indica si debe o no estar presente de manera obligatoria en un recurso QuestionnaireResponse en estado Completed. •type: campo que indica el tipo de componente que es el recurso. Puede ser texto, preguntas y otros grupos para anidar debajo de una pregunta o grupo. Según el tipo, el item podrá tener además una serie de parámetros que tendrán sentido: •answerOption: este campo es una lista de Questionnaire.QuestionnaireItemAnswerOptionComponent, el cual representa las posibles respuestas permitidas para la pregunta de tipo coding. Este campo a su vez tendrá los siguientes campos El componente Questionnaire.QuestionnaireItemAnswerOptionComponent del recurso Questionnaire representa una posible respuesta a una pregunta. Está formado por: •value: campo cuyo valor puede ser coding, que será el tipo de respuesta a a la pregunta. ‣system: la identificación UriType del sistema de códigos que define el significado del símbolo en el código. ‣code: símbolo codeType en sintaxis definida por el sistema. ‣display: representación String del significado del código en el sistema, siguiendo las reglas del sistema. Figura5: Diagrama UML del recurso FHIR Questionnaire junto con los componentes que lo forman [2] 2.7.1.2 Questionnaire Response El recurso QuestionnaireResponse está formado, entre otros, por los siguientes elementos [42], [2]: •resourceType: campo de tipo String que indica el tipo de recurso FHIR. En este caso el valor será QuestionnaireResponse. •identifier: campo que contiene el identificador del recurso en el servidor FHIR en el que se encuentra. 33
Capítulo 3 Solución desarrollada La base de datos seleccionada para la aplicación empresarial, en la que se almacenará toda la información relacionada con las instancias de proceso, es postgresql, aunque como se verá, durante el desarrollo se utilizó H2. Para almacenar todos los recursos FHIR que se manejan se hace uso de un servidor FHIR desplegado en un contenedor docker. Adicionalmente, se ha hecho uso de la herramienta Business Central para definir los procesos BPM (activos de negocio) que utiliza la aplicación. En la Figura8 se puede ver cómo queda la estructura de la aplicación diseñada. Figura8: Diagrama de componentes de la solución desarrollada para la gestión de consentimientos de pacientes La idea es que existan dos procesos BPM que definan el funcionamiento de la aplicación, un proceso de solicitud, en el que un facultativo solicita un consentimiento, y en el que existe un subproceso en el que un paciente revisa y acepta o rechaza el consentimiento solicitado. En el proceso de revisión, el facultativo genera una solicitud de consentimiento a partir de un formulario, definido en un recurso Questionnaire. La solicitud generada queda definida en un recurso FHIR QuestionnaireResponse, en el que se solicita para uno o más pacientes un consentimiento determinado. A partir de esas solicitudes se genera para cada uno de los pacientes un formulario de revisión (definido en un recurso FHIR Questionnaire, generado a partir del QuestionnaireResponse que ha generado el facultativo). Finalmente, en caso de que el consentimiento sea aceptado, este se materializa en un recurso FHIR de tipo QuesionnaireResponse, como respuesta al formulario, y en un de tipo Consent, que representa al consentimiento. En la siguiente imagen se puede ver de de manera gráfica la interacción detallada entre los usuarios y los distintos componentes de la aplicación. 40
Figura9: Diagrama de secuencia que muestra las interacciones entre los usuarios y los distintos componentes de la aplicación empresarial 3.1.1.1 Uso de la solución Para ejecutar la aplicación basta con ejecutar el script launch.sh del proyecto “gestorconsentimientos-service”: cd GestorConsentimientos/gestorconsentimientos-service/ ./launch.sh clean install Código19: Comando para la ejecución de la aplicación empresarial desarrollada En caso de querer utilizar la aplicación con otro perfil maven, que tendrá una serie de dependencias adicionales a las del perfil por defecto, basta con agregar al comando de ejecución la opción -Pnombre_perfil . Es, por ejemplo, lo que se hace para ejecutar la aplicación cuando se ha configurado spring para utilizar postgresql según la plantilla de configuración postgresql. Los perfiles maven están definidos en el fichero de configuración pom.xml . En el caso de querer utilizar la aplicación en modo desarrollador con Business Central, se puede configurar spring boot en el fichero application.properties según dice la plantilla del perfil «dev» application-dev.properties o llevar la configuración completa a la plantilla y utilizar el script launch-dev.sh , para que lance el programa utilizando el fichero de configuración del perfil de configuración spring dev . 41
Capítulo 3 Solución desarrollada Se puede comprobar el funcionamiento de la aplicación desde la url http://localhost:8090/ , que corresponde a la interfaz web de usuario, pudiendo acceder a cualquiera de los siguientes recursos disponibles: Endpoints generales: •/business-application/index.html : fichero estático de inicio por defecto al generarse el proyec- to. •/: bienvenida general •/menu : al acceder a este recurso se tiene que haber iniciado sesión, para lo que se tendrá en la sesión información sobre si el usuario es paciente o facultativo, para realizar la redirección adecuada al menú de rol, redirigiendo a /facultativo o /paciente . Endpoints pertenecientes al rol de facultativo médico: •/facultativo : menú •/facultativo/solicitar : formulario web para la creación de una solicitud de consentimiento •/facultativo/solicitudes : listado de las solicitudes de consentimiento pendientes de ser aprobadas y rechazadas •/facultativo/solicitudes/{id} : visualización de una solicitud de consentimiento concreta (que se puede aprobar o rechazar) •/facultativo/consentimientos : listado de los consentimientos que ha obtenido el facultativo •/facultativo/consentimientos/{id} : visualización de un consentimiento concreto Endpoints pertenecientes al rol de paciente: •/paciente : menú •/paciente/solicitudes : listado de las solicitudes de consentimiento que un paciente tiene pendientes de aprobar y rechazadas •/paciente/solicitudes/{id} : formulario que permite aceptar o rechazar una solicitud de consentimiento concreta, que se muestra en la misma página. •/paciente/consentimientos : listado de los consentimientos que ha proporcionado el paciente •/paciente/consentimientos/{id} : visualización de un consentimiento concreto Endpoints pertenecientes al rol de administrador: •/management/deployedUnits : se muestran en terminal todas las unidades de despliegue disponibles en el motor de procesos. •/management/processInstances?detailed : se muestran en terminal todas las instancias de proceso disponibles en el servidor, y en caso de indicar con «true» el parámetro «detailed», se mostrarán las tareas, variables y workItems de cada una de las instancias de proceso. •/management/workItems?processInstanceId : se muestran en terminal todos los workItems de una instancia de proceso. •/management/tasks?processInstanceId : se muestran en terminal todas las tareas de una instancia de proceso. •/management/vars?processInstanceId : se muestran en terminal todas las variables de una instancia de proceso. 3.2 Definición de procesos En este apartado se detallan los distintos procesos que implementa la aplicación, y que definen el flujo de ejecución que va a seguir. Tanto la aplicación como los procesos con los que trabaja, se han diseñado de forma que todos los objetos intermedios generados hasta el momento de consolidarse el consentimiento queden registrados, 42
quedando almacenados en el servidor FHIR en el que se apoya la aplicación empresarial, y sus identificadores en la base de datos asociada al motor de proceso, que persiste información de las instancias de proceso ejecutadas. Dicho esto, cuando un facultativo desee solicitar un consentimiento a un paciente, este construirá una solicitud a través de un formulario que le permita detallar el consentimiento que se va a solicitar. Ese formulario está definido en un recurso FHIR de tipo Questionnaire. La respuesta a ese formulario se materializará en un recurso FHIR de tipo Questionnaire Response, a partir del cual se mostrará al paciente lo que pide el facultativo, para que lo acepte, en otro formulario personalizado, y cuya respuesta (ambos materializados también en recursos FHIR) genera un recurso FHIR de tipo Consent. 3.2.1 Proceso de solicitud de consentimientos En el proceso RequestConsent, el facultativo que inicia el proceso pretende realizar una solicitud para obtener consentimiento sobre un asunto determinado, a uno o varios pacientes. Para elaborar esa solicitud de consentimiento, el facultativo va a responder a un cuestionario (recurso FHIR) en el que va a introducir todos los datos necesarios para la solicitud, como: • pacientes a los que va dirigida • datos a los que se busca acceder • periodo de acceso a los datos solicitados • facultativo que los va a utilizar • el uso que se le va a dar a los datos solicitados • … Lo que va a ocurrir a partir de la respuesta que el facultativo da a ese cuestionario, es que se va a generar una solicitud de consentimiento, con los datos e información indicados, que va destinada a los distintos pacientes seleccionados, los cuales tendrán que aceptar o rechazar la petición, generándose así el correspondiente consentimiento. El fin del proceso se producirá cuando cada uno de los pacientes haya ofrecido una respuesta a la solicitud de consentimiento que se ha generado para él. El proceso cuenta con las siguientes variables: 43
Capítulo 3 Solución desarrollada Tipo Nombre Descripción String practitioner facultativo que solicita el consentimiento String fhirServer servidor FHIR del que obtener el recurso Questionnaire que representa el cuestionario que asiste la creación de la solicitud del consentimiento Long requestQuestionnaireId id del recurso FHIR de tipo Questionnaire que representa el cuestionario que asiste la creación de la solicitud del consentimiento Long requestQuestionnaireResponseId id del recurso FHIR de tipo Questionnarie- Response que representa la respuesta al cuestionario que asiste la creación de la solicitud del consentimiento ArrayList <Long> patientList lista de identificadores de pacientes a los que va dirigida la solicitud de consentimiento ArrayList <Long> reviewList lista de identificadores de respuestas de los pacientes a su solicitud de consentimiento Tabla1: Variables del proceso de solicitud de consentimientos Figura10: Diagrama BPMN del proceso de solicitud de consentimientos El flujo de ejecución del proceso RequestConsent cuenta con los siguientes nodos: 1. Inicio 2. Tarea automática personalizada: ConsentRequestConfig 3. Tarea humana: ConsentRequestGeneration 4. Sub proceso: MultipleConsentReview 5. Fin 3.2.1.1 Inicio El facultativo médico comienza el proceso al querer crear una solicitud de consentimiento haciendo una operación GET al recurso /facultativo/solicitud , para lo que el controlador de la Business- Application utilizará la API del motor de procesos para crear una instancia del proceso, comenzando la ejecución del workflow, el cual empezará a partir del inicio. 44
3.2.1.2 Tarea automática personalizada La tarea ConsentRequestConfig cuenta con un WorkItemHandler personalizado que se encarga de recoger tanto el servidor FHIR (fhirServer) en el que se encuentra el recurso FHIR de tipo Questionnarie, el cual se utilizará para crear la solicitud de consentimiento, como el id (requestQuestionnaireId) que lo identifica. @Component("ConsentRequestConfig") public class ConsentRequestConfigHandler implements WorkItemHandler { private static final Logger logger = LogManager.getLogger(); @Value("${fhirserver.location}") private String fhirServer; @Value("${fhir.questionnaire.request.id}") private Long requestQuestionnaireId; @Override public void executeWorkItem(WorkItem workItem, WorkItemManager manager) { logger.info("IN --- WORK ITEM HANDLER: ConsentRequestConfigHandler"); logger.info("+ WI gestionado: " + workItem.toString()); Map<String,Object> params = null; Map<String,Object> results = new HashMap<String, Object>(); params = workItem.getParameters(); logger.info("+ Nombre proceso Entrada: "+(String)params.get("processName")); results.put("fhirServer",fhirServer); results.put("requestQuestionnaireId",requestQuestionnaireId); manager.completeWorkItem(workItem.getId(), results); logger.info("OUT --- WORK ITEM HANDLER: ConsentRequestConfigHandler"); } @Override public void abortWorkItem(WorkItem workItem, WorkItemManager manager) { } } Código20: Código del WorkItemHandler para la tarea «ConsentRequestConfig» Las variables de entrada: •RequestConsent (nombre del proceso) Las variables de salida: •fhirServer •requestQuestionnaireId 3.2.1.3 Tarea humana En la tarea ConsentRequestGeneration, se utilizan el servidor FHIR y el id de cuestionario para presentar el cuestionario seleccionado al facultativo. Cuando este lo rellena, lo entrega con una operación 45
Capítulo 3 Solución desarrollada POST al recurso «/facultativo/solicitud», y se almacena la petición, y se genera para cada uno de los pacientes una solicitud de consentimiento. Las variables que recibe: •practitioner •fhirServer •requestQuestionnaireId Las variables que rellena: •requestQuestionnaireResponseId •patientList •reviewList 3.2.1.4 Subproceso Para cada uno de los pacientes listados en el cuestionario, se genera un subproceso ReviewConsent en el que tienen que leer la solicitud de consentimiento y aceptarla o rechazarla. Su workflow cuenta con los siguientes nodos: 1. Inicio 2. Tarea humana: ConsentReviewGeneration 3. Fin Las variables de entrada: •practitioner •fhirServer •requestQuestionnaireId •requestQuestionnaireResponseId •patientList como patient Las variables de salida: •reviewList como review 3.2.1.5 Fin El proceso finaliza cuando cada uno de los pacientes listados ha respondido a su solicitud de consentimiento, terminando por consecuencia el sub proceso que le correspondía. 3.2.2 Proceso de revisión de consentimientos El proceso es iniciado por el proceso de Solicitud de consentimiento cuando el facultativo genera la solicitud de consentimiento para uno o varios pacientes. El proceso tiene las siguientes variables: 46
Tipo Nombre Descripción String practitioner facultativo que realiza la solicitud String fhirServer servidor FHIR Long requestQuestionnaireId id del recurso FHIR de tipo Questionnaire que representa el cuestionario que asiste la creación de la solicitud del consentimiento Long requestQuestionnaireResponseId id del recurso FHIR de tipo Questionnarie Response que representa la respuesta al cuestionario que asiste la creación de la solicitud del consentimiento String patient paciente que tiene que contestar a la solicitud de consentimiento Long reviewQuestionnaireId id del recurso FHIR de tipo Questionnarie que representa el cuestionario mostrado al paciente para que este acepte o rechace la solicitud de consentimiento Long reviewQuestionnaireResponseId id del recurso FHIR de tipo Questionnarie Response que representa la respuesta del paciente al cuestionario en el que acepta o rechaza la solicitud de consentimiento recibida Boolean review respuesta del paciente a la solicitud de consentimien- to Tabla2: Variables del proceso de revisión de consentimientos Figura11: Diagrama BPMN del proceso de revisión de consentimientos El flujo de ejecución del proceso RequestConsent cuenta con los siguientes nodos: 1. Inicio 2. Tarea humana: ConsentReviewGeneration 3. Fin 47
Capítulo 3 Solución desarrollada 3.2.2.1 Tarea humana En esta tarea, se utilizan el servidor FHIR y el id de la respuesta del cuestionario que identifica la solicitud de consentimiento generada por el facultativo para presentar al paciente el consentimiento que tiene que aceptar o rechazar. Cuando este lo rellena, lo entrega con una operación POST al recurso «/ facultativo/consentimiento». Las variables de entrada: •fhirServer •requestQuestionnaireResponseId Las variables de salida: •reviewQuestionnaireId •reviewQuestionnaireResponseId •review 3.3 Generación de la aplicación empresarial Tal y como queda documentado en la documentación oficial de jBPM [1], la creación de la aplicación comienza por su generación, a través de un arquetipo maven. Para la generación de este proyecto se han utilizado esos arquetipos con las opciones mostradas a continuación: mvn archetype:generate -B \ -DarchetypeGroupId=org.kie \ -DarchetypeArtifactId=kie-model-archetype \ -DarchetypeVersion=7.74.1.Final \ -DgroupId=us.dit \ -DartifactId=gestorconsentimientos-model \ -Dversion=1.0 \ -Dpackage=us.dit.gestorconsentimientos.model Código21: Comando para la generación del proyecto model de la aplicación empresarial Crea el proyecto que contiene el modelo de datos. mvn archetype:generate -B \ -DarchetypeGroupId=org.kie \ -DarchetypeArtifactId=kie-kjar-archetype \ -DarchetypeVersion=7.74.1.Final \ -DgroupId=us.dit \ -DartifactId=gestorconsentimientos-kjar \ -Dversion=1.0 \ -Dpackage=us.dit.gestorconsentimientos Código22: Comando para la generación del proyecto KJAR de la aplicación empresarial Crea el proyecto KJAR, que contiene los activos de negocio. 48
mvn archetype:generate -B \ -DarchetypeGroupId=org.kie \ -DarchetypeArtifactId=kie-service-spring-boot-archetype \ -DarchetypeVersion=7.74.1.Final \ -DgroupId=us.dit \ -DartifactId=gestorconsentimientos-service \ -Dversion=1.0 \ -Dpackage=us.dit.gestorconsentimientos.service \ -DappType=bpm Código23: Comando para la generación del proyecto service de la aplicación empresarial Crea el proyecto servicio, en el que está desarrollada la aplicación empresarial. Para que la aplicación generada funcione correctamente, es necesario modificar la versión de spring que utiliza la aplicación a la 2.6.15. Para ello se va a incluir en el fichero pom.xml del proyecto gestorconsentimientos-service un padre que define la versión para cualquier dependencia y plugin spring a la 2.6.15. <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.15</version> <!-- Referencia de la versión 2.6.15: https://docs.spring.io/spring-boot/docs/ 2.6.15/reference/htmlsingle/ --> </parent> Código24: Fragmento de configuración POM que define un padre con la versión de Spring a utilizar Como se va a trabajar con Hapi FHIR, se van a definir e incluir las dependencias necesarias, se trabaja en la versión «6.4.0», la versión más actual en la que no existen problemas de compatibilidad, puesto que el procesamiento XML de FHIR utilizará la implementación StAX «Woodstox» versión «6.4.0», utilizar versiones superiores para estas dependencias genera un fallo al trabajar con ellos. <!-- Hapi FHIR --> <dependency> <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-structures-r5</artifactId> <version>${hapifhir.version}</version> </dependency> <dependency> <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-base</artifactId> <version>${hapifhir.version}</version> </dependency> <dependency> <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-client</artifactId> <version>${hapifhir.version}</version> </dependency> Código25: Fragmento de configuración POM que define las dependencias FHIR necesarias en el proyecto 49
Capítulo 3 Solución desarrollada 3.6 Api de Servicios del motor jBPM Para el uso del motor jBPM en la aplicación empresarial se va a hacer uso de las diferentes llamadas de la API java de servicios, que presenta una serie de servicios, disponibles en Spring para la inyección de dependencias. Estos servicios permiten gestionar los diferentes objetos que existen en el motor, como las Unidades de Despliegue, Instancias de Procesos, tareas y workItems. A pesar de estas facilidades, la obtención de determinados elementos puede ser un tanto compleja de abordar. Para comenzar a crear instancias de procesos en el motor, será necesario contar con una unidad de despliegue activa en la que se van a ejecutar todas las instancias que se vayan generando de los procesos del proyecto KJAR que representa esa Unidad de Despliegue. Después, será posible interactuar con los procesos, para obtener sus tareas, obtener los Work Item activos del proceso (que pertenecen a las tareas activas de este) y obtener variables internas. En cuanto a las tareas, será posible modificar su estado, obtener sus variables internas y más información. 3.6.1 Creación de Unidades de Despliegue La creación de unidades de despliegue se puede abordar de varias maneras: • De manera dinámica, a través de código que tiene lugar en el flujo de ejecución de la aplicación empresarial. • De manera estática, con una configuración establecida que la aplicación empresarial se encarga de desplegar en el arranque. La primera opción requiere el uso del servicio DeploymentService para su despliegue, y es necesario que la aplicación empresarial esté diseñada para que cualquier instanciación de un proceso se produzca de manera posterior al despliegue de la unidad de despliegue que corresponde al proyecto KJAR de ese proceso. La segunda opción no requiere el desarrollo de la lógica para el despliegue, pero sí que requiere la creación de una configuración de unidades de despliegue que la aplicación empresarial tomará al iniciarse. Esta es la opción que se ha implementado en el proyecto. La configuración está disponible en un fichero con el nombre gestorconsentimientos-service.xml , que es el identificador del motor de procesos que despliega la aplicación empresarial. Esto se encuentra fijado en la configuración Spring de la aplicación empresarial: # Identificador de la instancia de motor de procesos desplegada kieserver.serverId=gestorconsentimientos-service # Nombre de la instancia de motor de procesos desplegada kieserver.serverName=gestorconsentimientos-service Código29: Configuración Spring que establece el identificador y nombre del motor de procesos que despliega la aplicación empresarial De manera independiente a cualquiera de las dos opciones, al instanciar los procesos, estos tienen que seleccionar la unidad de despliegue en la que se van a instanciar, para lo que tienen que disponer del identificador de la unidad de despliegue. De manera predeterminada el identificador y nombre de las 56
unidades de despliegue que se instancian, están formados por el grupo, identificador de artefacto y versión del proyecto al que debe representar. 3.6.2 Interacción de Procesos Para manejar los procesos, como cada uno es diferente, lo que se ha hecho es crearles un servicio («consentRequestProcessService» y «consentReviewProcessService») que se va a encargar de manejarlos de manera independiente, con métodos para instanciarlos y trabajar con las diferentes tareas que tienen, modificando sus estados y obteniendo variables relevantes para el trabajo. Para instanciar un proceso lo que se hace es utilizar el método startProcess del servicio ProcessService , que toma el identificador de la unidad de despliegue que contiene desplegados los activos del proyecto KJAR de la aplicación, el ID de la definición de proceso que se quiere instanciar, que tiene que estar presente entre los activos que hay en la unidad de despliegue, y los parámetros de entrada que necesita el proceso para funcionar. Este proceso devolverá el identificador de instancia de proceso que corresponde la instancia de proceso generada. // Instanciación del proceso processInstanceId = processService.startProcess( "gestorconsentimientos-kjar", processId, params); Código30: Uso del servicio «processService» para la instanciación de un proceso Los procesos que estén diseñados para ser instanciados por otros procesos, ejerciendo el rol de subprocesos, no será necesario que cuenten con un método para que la aplicación los instancie, puesto que lo hará el motor de procesos cuando sea necesario. Sin embargo, sí que seguirán siendo necesarios los métodos que gestionan las tareas. 3.6.3 Interacción con tareas Dentro de los procesos hay un conjunto de actividades, llamadas tareas, que se van a ir ejecutando como parte del workflow del proceso. En función del tipo de tarea, esta ejecución se llevará a cabo de una manera u otra. Por un lado están las tareas que se ejecutan de manera automática, las cuales tienen una carga de trabajo asociada (Work Item), que es procesada por un manejador en la aplicación empresarial. Estas tareas automáticas, según el tipo de tarea del que se trate, tendrán un manejador u otro para que se procese su Work Item. En caso de no estar implementado ese manejador se puede definir, lo que permite definir tareas personalizadas con su lógica de trabajo. Y de manera opuesta a las automática, están aquellas que necesitan la interacción humana para ser llevadas a cabo. Esta interacción humana se llevará a cabo a través de la aplicación empresarial, por lo que a partir de lo que los usuarios hagan en la aplicación, la aplicación gestionará esas tareas. Para interactuar con las tareas humanas, se emplea el servicio UserTaskService , el cual ofrece un abanico de métodos para gestionar una tarea concreta. Algunas de estas acciones son: • Modificación de su estado: empezarla, finalizarla, pararla, dejarla en estado fallido, abortada… • Modificación de otros atributos: usuario asignado, prioridad, comentarios, relaciones, fecha de expiración, descripción • Obtención de variables de la tarea Sin embargo, para llegar a utilizar este servicio para trabajar con una tarea, hay que conseguir la tarea, lo cual se puede conseguir empleando el servicio RuntimeDataService , que permite obtener datos relacionados con la ejecución. 57
Capítulo 3 Solución desarrollada Cuando se va a llevar a cabo una tarea humana en la aplicación empresarial, el controlador utiliza el servicio que se ha creado para la sesión del proceso, y a partir del identificador de la instancia de proceso, se obtiene la tarea y se inicia. // Obtención del cuestionario a mostrar vars = consentRequestProcess.initRequestTask(processInstanceId); Código31: Llamada al método de los servicios que se han desarrollado para gestionar los procesos, que permite inicializarlo Para llegar a la tarea, lo único de lo que se dispone es del identificador de la instancia de proceso a la que pertenece, el conocimiento de que cuando se llame a este método la tarea es la siguiente para ser ejecutada en el workflow de la instancia (por diseño), y el nombre de la tarea. Lo que se hace es obtener la lista de los work item de la instancia de proceso, que están disponibles para ser procesados (empleando el servicio ProcessService ), y a partir de la lista se obtiene el que corresponda a la tarea que se quiere comenzar. Esto se lleva a cabo obteniendo el primer work item que pertenezca a una tarea como la que se quiere comenzar. Se hace esta comprobación por conseguir robustez, pero por el diseño que tiene el flujo de trabajo del proceso no es posible obtener más de un WorkItem de manera simultánea. Una vez se tiene el work item que pertenece a la tarea, se puede utilizar el servicio RuntimeDataService para obtener la tarea a la que pertenece ese Work Item, y emplear el servicio UserTaskService para gestionarla. // La instancia de Work Item que se está obteniendo es la primera de las múltiples posibles tareas activas que puede haber en el proceso, por cómo está diseñado, solo puede haber una única tarea o nodo activo de manera simultánea, y por tanto un solo WI en esa lista. workItemInstance = workItemInstanceList.stream().filter( wi->wi.getParameter("TaskName").equals("ConsentRequestGeneration") ).collect(Collectors.toList()).get(0); userTaskInstanceDesc = runtimeDataService.getTaskByWorkItemId( workItemInstance.getId() ); userTaskService.start(userTaskInstanceDesc.getTaskId(),practitioner); Código32: Fragmento de código java que obtiene el identificador de una tarea pendiente para una instancia de proceso, y la empieza Lo que se ha hecho ha sido a partir del identificador de la instancia de proceso y el nombre de la tarea, recibir la tarea a través del Work Item. Sin embargo, se podría dotar de mayor autonomía al concepto de tarea, y tratar de obtenerla sin emplear el work item que tiene asociado, empleando unicamente el nombre y el identificador de la instancia de proceso, sin embargo, esto podría requerir que el nombre o identificador de la definición de la tarea sea único para todos los activos de negocio. A la hora de gestionar la tarea, es importante tener autorización para ello, por lo que únicamente el usuario facultativo que ha iniciado el proceso, y que ha pasado su nombre con la variable practitioner , y que la tarea recibirá como ActorID , podrá comenzar y completar la tarea. Es necesario recoger las variables necesarias para llevar a cabo la tarea, lo cual es posible hacerse de varias maneras, todo depende de lo que se considere más elegante y de cómo se defina el proceso en los activos de negocio: 58
• A partir de las variables del proceso, empleando el servicio ProcessService , que es lo que se ha hecho en el proyecto. • A partir de las variables de la propia tarea, obteniéndolas del Work Item, o de la propia tarea utilizando algún servicio. // Las variables que indican el servidor FHIR y el id que referencian al recurso FHIR Questionnaire que se utiliza para generar la solicitud de consentimiento, se obtienen actualmente de las variables del proceso, sin embargo se podría hacer de otras maneras, por ejemplo a través del WorkItem, o de la tarea, de sus variables de entrada. Hay que tener claro que cada enfoque puede tener sus pros y contras, y que implicará distintos caminos e información previa fhirServer = (String) processService.getProcessInstanceVariable( processInstanceId, "fhirServer"); requestQuestionnaireId = (Long) processService.getProcessInstanceVariable( processInstanceId, "requestQuestionnaireId"); vars.put("fhirServer",fhirServer); vars.put("requestQuestionnaireId",requestQuestionnaireId); return vars; Código33: Fragmento de código java que se encarga de obtener las variables de un proceso para tenerlas al ejecutar una tarea A la hora de completar la tarea que se ha arrancado, es necesario, con el método correspondiente, mandar la colección de variables que necesita como resultado de la ejecución de la tarea en la aplicación empresarial. 3.6.4 Consultas En la solución desarrollada se ha dotado a la aplicación empresarial de la capacidad de que los usuarios puedan ejecutar una serie de consultas sobre las instancias de procesos del motor jBPM. El objetivo de esto es que se pueda obtener información sobre las solicitudes de consentimiento y los consentimientos existentes, tanto por parte de los pacientes para obtener qué solicitudes de consentimiento tienen que revisar y qué consentimientos han otorgado, como por parte de los facultativos, para ver qué solicitudes de consentimiento han emitido, y que consentimientos han obtenido. Esto se lleva a cabo realizando consultas al motor de procesos para que devuelva esta información, puesto que está presente a modo de histórico de los procesos y sus variables, aunque a la hora de llevarlo a la práctica, puede resultar complejo identificar qué servicio de la API utilizar, para poder hacer las peticiones necesarias para obtener los datos referentes a: • Solicitudes de consentimiento: peticiones de consentimiento que los facultativos han generado utilizando el proceso de solicitud de consentimiento, y que no han sido aceptadas por los pacientes, lo que hace que no se haya llegado a convertir en un consentimiento. • Consentimientos generados: son las solicitudes de consentimiento aceptadas por los pacientes. Para encontrar las solicitudes de consentimiento, lo que se busca es un proceso de revisión de consentimientos que haya sido creado, lo que significa que la solicitud de consentimiento existe por su proceso padre, y que además el proceso de revisión de consentimientos no haya acabado (ni el proceso padre por consecuencia) por no haberse finalizado la tarea de revisión de la solicitud del consentimiento. Para encontrar los consentimientos otorgados, lo que se hace es buscar un proceso de revisión de consentimientos, que haya finalizado. 59
Capítulo 3 Solución desarrollada Para localizar estos elementos para un facultativo, lo que se hace es verificar que el proceso de solicitud de consentimiento lo haya llevado a cabo este, ya sea por la propiedad de una tarea, o por la variable que lo indique del proceso, no existe una única solución para esto. Y para localizar los elementos para un paciente, se deberá comprobar que el proceso de revisión lo tenga que llevar a cabo el paciente, lo que se debe hacer en el proceso de revisión y no de solicitud, porque según cómo se implemente el cuestionario para crear la solicitud, y según se defina el proceso, se podrán generar distintas solicitudes de consentimiento a partir de un solo cuestionario (proceso de solicitud). Entender esta relación ayuda a comprender cómo se han llevado a cabo el uso de las APIs para tratar de hacer las consultas. La implementación de estas consultas se ha efectuado de distintas maneras, unas presentaban problemas, y otras eran excesivamente complejas. Finalmente se ha utilizado el mecanismo más «elegante» que es el que se va a comentar en este apartado. Las implementaciones que se realizaron de manera previa se encuentran descritas en el ApéndiceC. Este servicio presenta una serie de métodos que se basan en el servicio QueryService y que llevan a cabo toda la lógica necesaria para utilizar consultas. El servicio ofrece una forma rápida y sencilla de llevar a cabo una serie de consultas comunes y de bastante interés para los desarrolladores, utilizando de manera interna el servicio QueryService . Puesto que la consulta que queremos hacer entra dentro de ese conjunto de consultas que implementa, es posible utilizarlo para obtener instancias de proceso, utilizando para filtrar aquellas que son de interés, elementos como tareas, atributos de instancia, variables de las instancias y las propias instancias a través de su identificador. Además de basarse en el servicio QueryService , que aporta robustez y acceso a todos los datos de la BD (cosa que no ocurre en todos los servicios disponibles), el servicio puede utilizarse sin problemas para realizar consultas sin importar si se utilizan como elementos de interés los procesos o las tareas, pudiéndose utilizar el método QueryUserTasksByVariables en vez del empleado, o utilizar otros objetos QueryParam para filtrar. En el siguiente fragmento de código se puede ver la primera implementación estable que se ha llevado a cabo en el proyecto, pero que a pesar de todo, contiene un fallo que más adelante se explica cómo se ha arreglado: public List <ReviewedConsent> getConsentsByPractitioner(String practitioner){ List<ProcessInstanceWithVarsDesc> processInstanceParentDescList = null; final List<Long> processInstanceIdRequestList = new ArrayList<String>();; List<ProcessInstanceWithVarsDesc> processInstanceDescList = null; Map<String,Object> processInstanceVars = null; List <ReviewedConsent> consentsList = new ArrayList<ReviewedConsent>(); // Lista con los ID de instancias de procesos de "ConsentRequest" iniciados por <practitioner> processInstanceParentDescList = advancedRuntimeDataService.queryProcessByVariablesAndTask( new ArrayList<QueryParam>(){{ add(QueryParam.equalsTo(AdvanceRuntimeDataService.PROCESS_ATTR_DEFINITION_ID, processConsentRequestId)); add(QueryParam.equalsTo(AdvanceRuntimeDataService.TASK_ATTR_STATUS, requestHumanTaskName)); add(QueryParam.equalsTo(AdvanceRuntimeDataService.TASK_ATTR_STATUS, Status.Completed.toString())); 60
}}, new ArrayList<QueryParam>(){{ add(QueryParam.equalsTo("practitioner", practitioner)); }}, null, (QueryParam) null, new QueryContext()); for (ProcessInstanceWithVarsDesc pi:processInstanceParentDescList){ processInstanceIdRequestList.add(pi.getId()); } // Evita una excepción en caso de que no haya ningún proceso aún if (!processInstanceIdRequestList.isEmpty()){ // Lista de instancias de procesos "ConsentReview", cuyos padres fueron iniciados por <practitioner> processInstanceDescList = advancedRuntimeDataService.queryProcessByVariablesAndTask( new ArrayList<QueryParam>(){{ add(QueryParam.in(QueryResultMapper.COLUMN_PARENTPROCESSINSTANCEID, processInstanceIdRequestList)); add(QueryParam.equalsTo(AdvanceRuntimeDataService.PROCESS_ATTR_DEFINITION_ID, processConsentReviewId)); add(QueryParam.equalsTo(AdvanceRuntimeDataService.TASK_ATTR_NAME, reviewHumanTaskName)); add(QueryParam.equalsTo(AdvanceRuntimeDataService.TASK_ATTR_STATUS, Status.Completed.toString())); }}, null, null, (QueryParam) null, new QueryContext()); } // Obtención de las variables que forman parte de cada consentimiento for (ProcessInstanceWithVarsDesc processInstanceDesc: processInstanceDescList) { processInstanceVars = processInstanceDesc.getVariables(); consentsList.add(new ReviewedConsent( (Long) processInstanceDesc.getId(), (String) processInstanceVars.get("fhirServer"), (Long) Long.parseLong( (String) processInstanceVars.get("requestQuestionnaireId")), (Long) Long.parseLong( (String) processInstanceVars.get("requestQuestionnaireResponseId")), (Date) processInstanceDesc.getDataTimeStamp(), (String) processInstanceVars.get("practitioner"), (String) processInstanceVars.get("patient"), (Long) Long.parseLong( (String) processInstanceVars.get("reviewQuestionnaireId")), (Long) Long.parseLong( (String) 61
Capítulo 3 Solución desarrollada processInstanceVars.get("reviewQuestionnaireResponseId")), (Boolean) processInstanceVars.get("review"))); } } return consentsList; Código34: Fragmento de código con la primera implementación planteada para la obtención de los consentimientos obtenidos por un facultativo, haciendo uso del servicio «AdvancedRuntimeDataService» Lo que se hace en primer lugar es obtener todos los procesos de tipo Solicitud de consentimiento que pertenezcan al facultativo que hace la solicitud, y después todos los procesos de revisión de consentimiento, que han acabado, y cuyo padre está en la lista de procesos anterior. Para hacer esto lo que se ha utilizado es el método queryProcessByVariablesAndTask , el cual obtiene objetos de tipo ProcessInstanceWithVarsDesc , que contienen todos los datos de una instancia de proceso, además de las variables que tiene el proceso. Este método admite como parámetros objetos de tipo QueryContext y QueryParam . Los objetos de tipo QueryContext permiten controlar aspectos del resultado de la petición como ordenar los datos o limitar la cantidad, mientras que los objetos de tipo QueryParam trabajan más sobre la consulta, permitiendo configurar la consulta y obtener un resultado filtrado según lo que se desee obtener. Los objetos QueryParam lo que permiten en definitiva es configurar cualquier forma de filtrado de una consulta SQL, sin tener que hacerla a mano, soportando filtrado por comparación de variables con uno o varios valores, o comprobando que una determinada variable esté presente. Introducidos estos conceptos, el método admite: • Un solo objeto de QueryContext , puesto que solo habrá un resultado. • Distintos argumentos de filtrado (objetos de tipo QueryParam ): ‣ Una lista para filtrar por los atributos de las instancias de procesos. ‣ Una lista para filtrar por las variables de las instancias de procesos. ‣ Una lista para filtrar por las variables de las tareas de las instancias de procesos. ‣ Un único objeto para filtrar por el propietario potencial de la instancia del proceso. Es necesario notar que en esta última implementación, todas las variables que se quieren recabar para formar un consentimiento, como el facultativo, el paciente, los id de los distintos recursos FHIR, se encuentran en el proceso hijo, el de tipo «revisión de consentimientos». Esto se ha hecho a conciencia para solucionar un obstáculo que presenta el funcionamiento del servicio, y es que cuando se realiza una consulta, y se utilizan algunas variables para filtrar el resultado, estas variables que se han utilizado, no aparecen en las instancias de procesos del resultado de la consulta. Esto supone un problema cuando las variables que se quieren recolectar para un consentimiento, están repartidas tanto en el proceso padre, de tipo «solicitud de consentimientos», como en el subproceso de tipo «revisión de solicitud de consentimiento» que tiene lugar en el anterior, ya que una vez se tiene el subproceso, este no se podía relacionar con su instancia de proceso padre, que es de tipo «solicitud de consentimiento», porque el sub proceso utilizaba el identificador del padre en el filtro, y ya no estaba presente en los resultados. Como solución, para evitar tener que realizar otra consulta para obtener las variables de la instancia del proceso padre, se opta por modificar la definición de los procesos, de modo que todas las variables 62
que van a estar relacionadas con un consentimiento, se encuentran en el sub proceso de tipo «revisión de consentimientos». Una vez se encontraba en funcionamiento toda la lógica de las consultas, basándose en el servicio AdvancedRuntimeDataService , se decidió modificar la configuración de Sprint Boot para dejar de utilizar la base de datos H2, orientada a pruebas, y comenzar a utilizar la base de datos postgresql, más profesional. Este cambio produjo la aparición de nuevos problemas. El primero estaba asociado a los tipos de los datos admitidos por las BBDD, y es que postgresql es sensible al tipo de los objetos de la BD que se manejan en los métodos, mientras que cuando se utiliza la BD H2 esto no sucede, por lo que es buena práctica siempre que sea posible consultar cómo es el modelo de datos accediendo a la BD, y ver los tipos de las distintas columnas de las tablas que se vayan a utilizar. Este detalle surgió al manejar los identificadores de las instancias de proceso, que en la BD están como numéricos, y se estaban manejando como strings. Y el segundo, resultó en un fallo que aparentemente es de funcionamiento del motor de procesos, dado que lo que funcionaba en H2, funcionaba correctamente en postgresql, pero consiguiendo como resultado únicamente el primer elemento. Se realizaron pruebas, se depuró el código, y se pasó a simplificar las consultas, empleando menos elementos de filtrado que en la consulta que se llevaba a cabo antes, de forma que el resultado era más amplio. Esto dió lugar al código funcional que se presenta en el siguiente fragmento de código: 63
Capítulo 3 Solución desarrollada public List <ReviewedConsent> getConsentsByPractitioner(String practitioner){ logger.info("Obteniendo los consentimientos pendientes de "+practitioner); List<ProcessInstanceWithVarsDesc> processInstanceDescList = null; Map<String,Object> processInstanceVars = null; List <ReviewedConsent> consentsList = new ArrayList<ReviewedConsent>(); // Lista de instancias de procesos "ConsentReview", cuyos padres fueron iniciados // por <practitioner> //runtimeDataService.getProcessInstancesByVariable(practitioner, null, null); processInstanceDescList = advancedRuntimeDataService.queryProcessByVariables( new ArrayList<QueryParam>(){{ add(QueryParam.equalsTo( AdvanceRuntimeDataService.PROCESS_ATTR_DEFINITION_ID, (String) processConsentReviewId) ); add(QueryParam.equalsTo("status", 2)); }}, new ArrayList<QueryParam>(){{ add(QueryParam.equalsTo("practitioner", (String) practitioner)); }}, new QueryContext()); // Obtención de las variables que forman parte de cada consentimiento for (ProcessInstanceWithVarsDesc processInstanceDesc: processInstanceDescList){ processInstanceVars = runtimeDataService.getVariablesCurrentState( processInstanceDesc.getId()).stream().collect( Collectors.toMap((VariableDesc::getVariableId), VariableDesc::getNewValue) ); consentsList.add(new ReviewedConsent( (Long) processInstanceDesc.getId(), (String) processInstanceVars.get("fhirServer"), (Long) Long.parseLong( (String) processInstanceVars.get("requestQuestionnaireId")), (Long) Long.parseLong( (String) processInstanceVars.get("requestQuestionnaireResponseId")), (Date) processInstanceDesc.getDataTimeStamp(), (String) processInstanceVars.get("practitioner"), (String) processInstanceVars.get("patient"), (Long) Long.parseLong( (String) processInstanceVars.get("reviewQuestionnaireId")), (Long) Long.parseLong( (String) processInstanceVars.get("reviewQuestionnaireResponseId")), (Boolean) Boolean.parseBoolean( (String) processInstanceVars.get("review")), (Long) Long.parseLong("0"))); //TODO Extraer del proceso el ID del consentimiento generado a partir de reviewQuestionnaireResponseId } return consentsList; } Código35: Método que se utiliza para formar una entidad de tipo consentimiento a través de todas las variables que tiene asociadas en el motor de procesos 64
3.7 Antecedentes de desarrollo En un proyecto anterior a este, realizado por Jose Antonio García Linares se comenzó a trabajar en una solución para este caso de uso, y se planteó una arquitectura similar, sobre la que se han realizado una serie de cambios para solucionar problemas. Comenzando por la arquitectura de la solución, se planteó el uso de un servidor FHIR público, lo cual estaba bien para la etapa de desarrollo, pero una vez se mandara a producción la solución, no era una opción viable al tratarse de un servidor público al que todo el mundo tiene acceso. Además, este presentaba problemas de disponibilidad con bastante frecuencia, lo que suponía un problema incluso a la hora de desarrollar y realizar pruebas. Otro punto problemático que se detectó, es que no era óptimo pasar los recursos FHIR completos al motor de procesos, puesto que la cantidad de información que contienen ralentizaba el funcionamien- to, llegando incluso a bloquear el motor. Esto se ha modificado y lo que se hace es trabajar con los identificadores de los recursos, que se encuentran disponibles en el servidor FHIR. Además de los cambios realizados para solucionar los problemas, el proyecto se ha desarrollado desde cero, y se ha hecho uso de la API de servicios de jBPM para interactuar con el motor de procesos e implementar consultas de solicitudes y consentimientos, a diferencia de lo que se había hecho en el proyecto anterior, en el que se hacía uso de la librería KIE org.kie.server . 65
Capítulo A Entorno de desarrollo update-alternatives --list java update-alternatives --list javac Código38: Comando para comprobar qué versiones de java están disponibles Para comprobar cuál es la versión instalada que se va a utilizar por defecto (queremos la 17): java --version Código39: Comando para obtener la versión java disponible en Linux En caso de estar instalado y experimentar problemas, estos pueden estar relacionados con las variables de entorno definidas por java, y que contienen las rutas de instalación de los diferentes complementos. A.2 Maven Maven [18] es la herramienta que va a emplearse para la gestión (en términos de software) del proyecto, que contiene la BA, además de como repositorio de artefactos local. Para comprobar si maven está instalado: mvn -v Código40: Comando para obtener la versión de maven instalada en Linux Es posible identificar la versión que el gestor de paquetes apt nos va a instalar, utilizando el comando: apt list | grep maven/ Código41: Comando para identificar la versión maven disponible para instalar en Linux Para instalar maven utilizando el gestor de paquetes apt: sudo apt-get update sudo apt-get upgrade sudo apt install maven mvn -v Código42: Fragmento de código bash para instalar maven en Linux utilizando el gestor de paquetes apt Para consultar las opciones disponibles: mvn --help Código43: Comando para obtener ayuda sobre la herramienta maven A.3 Git Es un sistema de control de versiones distribuido, el cual va a permitir mantener un control de versiones para el proyecto de la BA. Otra razón que lleva a ser necesario el uso de Git, es que se trata del mecanismo utilizado para gestionar la definición de procesos, del proyecto «consentimientos-kjar» al módulo «business-central» de la 72
aplicación «jBPM». Esto hace posible gestionar por completo esas definiciones de procesos que forman parte del funcionamiento de la aplicación de negocios (BA), desde jBPM; no solo para poder diseñarlos y modificarlos, si no para poder monitorizar y analizar el funcionamiento de la BA al ejecutarla en modo development. Para comprobar si GIT está instalado: git --version Código44: Comando para obtener la versión de GIT instalada en Linux Es posible identificar la versión que el gestor de paquetes apt nos va a instalar, utilizando el comando: apt list | grep git/ Código45: Comando para obtener la versión disponible de GIT para instalar en Linux Para instalar GIT utilizando el gestor de paquetes apt: sudo apt-get update sudo apt-get upgrade sudo apt install git git -v Código46: Fragmento de código bash para instalar GIT en Linux Para consultar las opciones disponibles: git --help Código47: Comando para obtener ayuda sobre el uso de la herramienta GIT Para configurar GIT: git config --global user.name <name> git config --global user.email <email> # En algunas situaciones puede ser útil modificar la configuración desde el archivo nano ~/.gitconfig Código48: Fragmento de código para configurar la herramienta GIT De forma alternativa se puede configurar GIT para otro ámbito que no sea global. A.4 IDE El uso de un IDE es totalmente opcional, pudiendo escoger cualquiera. Algunos conocidos son: • Eclipse • IntelliJ Idea • Visual Studio Code Además cada uno cuenta con distintas extensiones para facilitar el trabajo con algunas de las tecnologías que se han empleado: • Lenguaje java 73
Capítulo A Entorno de desarrollo • Framework Spring • Thymeleaf • jBPM • Lenguaje BPMN • Git • Maven Para el IDE Visual Studio Code ser recomienda la extensión “TODO Tree” que realiza una lista con todos los comentarios TODO, FIXME y WARNING del proyecto. A.5 Docker Docker es un programa que ofrece virtualización a nivel de sistema operativo o basada en contenedores. No es estrictamente necesario, pero puede ser utilizado para dos situaciones: • El despliegue de la herramienta Business-Central puede hacerse utilizando un contenedor preparado para su ejecución. • El despliegue de un servidor FHIR para evitar tener que utilizar los servidores de prueba públicos que hay disponibles, los cuales pueden generar fallos. La instalación de docker está perfectamente documentada en su página web [49], para cada sistema operativo y variando entre distintas distribuciones Linux, así como diferentes métodos de instalación y configuración. La instalación empleada en este proyecto es la siguiente: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done # Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/ docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo docker run hello-world Código49: Bloque de comandos para llevar a cabo la instalación de Docker 74
A.6 Postgresql Es un sistema gestor de bases de datos relacionales y una de las más interesantes a la hora de funcionar en producción. Su uso no es obligatorio, pero es una de las opciones disponibles. Esto es así debido a que la aplicación empresarial (BA) está desarrollada con Spring Boot, por lo que se apoya en JPA para los aspectos de almacenamiento, lo que permite el uso de distintos gestores de bases de datos. Para comprobar si postgresql está instalado: psql -V Código50: Comando para obtener la versión de postgresql instalada Es posible identificar la versión que el gestor de paquetes apt nos va a instalar, utilizando el comando: apt list | grep postgresql/ Código51: Comando para obtener la versión de postgresql disponible para instalar Para instalar postgresql utilizando el gestor de paquetes apt: sudo apt-get update sudo apt-get upgrade # servidor sudo apt install postgresql # usuario sudo apt install postgresql-client psql -V Código52: Bloque de comandos para instalar postgresql en Linux Para consultar las opciones disponibles: psql --help Código53: Comando para obtener ayuda sobre el comando psql Durante la instalación, se crea una cuenta por defecto llamada postgres , la cual tiene todos los permisos asignados. Para utilizar así la cuenta por defecto: sudo -i -u postgres psql \du \q Código54: Bloque de comandos para la apertura de postgresql La creación de nuevos usuarios y bases de datos se puede realizar de diferentes maneras, ya sea mediante una serie de herramientas que incluye postgresql, o desde psql. Es necesario hacerlo desde una cuenta con permisos, como la cuenta por defecto. Los siguientes comandos permiten la creación de un usuario para acceder a una base de datos concreta del servidor postgresql, utilizando las utilidades que incluye postgresql. 75
Capítulo A Entorno de desarrollo # Creación del nuevo usuario sudo -u postgres createuser <username> # Creación de la base de datos sudo -u postgres createdb <dbname> # Asignando una contraseña al usuario creado sudo -u postgres psql alter user <username> with encrypted password '<password>'; \q # Asignando al nuevo usuario todos los privilegios sobre la nueva BD sudo -u postgres psql grant all privileges on database <dbname> to <username> ; \q Código55: Bloque de comandos para llevar a cabo la creación de un usuario y base de datos en postgresql Para conectarse a la base de datos: psql -h <host> -U <usuario> -d <dataBase> Código56: Comando para conectarse a una base de datos en postgresql A.6.1 Instalación en un contenedor Docker Es posible desplegar un contenedor docker que contiene un servidor postgresql, el cual es posible configurar mediante variables de entorno que va a tener el contenedor. El siguiente fragmento de código pertenece a un ejemplo de docker compose con el que se podría desplegar: 76
--- services: jbpm_db: image: postgres container_name: jbpm_db ports: - 5432:5432 shm_size: 128mb # set shared memory limit when using docker-compose # [README] Al descomentar esta sección, se consigue persistencia de los datos de la BD #volumes: # - data:/var/lib/postgresql environment: POSTGRES_DB: consentimientos POSTGRES_USER: jbpm POSTGRES_PASSWORD: jbpm restart: no # Servicio en el puerto 3000 que permite conectarse a la BD de postgresql, utilizando la dirección jbpm_db y el puerto 5432, al estar dentro de la misma red docker. adminer: image: adminer container_name: adminer ports: - 3000:8080 restart: no Código57: Docker compose para el despliegue de los contenedores postgresql y adminer En caso de querer que los datos de la base de datos se persistan entre borrados de contenedor es posible montar un volumen en el que los datos queden almacenados. Si el contenedor no es borrado y solo es pausado, los datos quedan en el estado del contenedor. La base de datos se crea de manera automática sin necesidad de interactuar con el servidor. El servicio adminer permite conectarse a la base de datos y poder visualizar el contenido, permitiendo poder visualizar el modelo de datos de las aplicaciones que la utilicen. A.7 Servidor FHIR Dado que la aplicación requiere de un servidor que implemente el estándar FHIR, se ha optado por utilizar un servidor FHIR. Es posible localizar servidores públicos Hapi para poder realizar pruebas, sin embargo, estos pueden no estar siempre disponibles, por lo que lo cómodo y robusto es desplegar uno por cuenta propia. Para desplegar una instancia de servidor FHIR se va a utilizar una imagen docker. Con el siguiente docker compose es posible desplegarlo: 77
Capítulo A Entorno de desarrollo --- services: hapi: image: hapiproject/hapi:v7.0.3 container_name: hapi ports: - 8080:8080 volumes: - ./application.yaml:/app/config/application.yaml restart: no Código58: Docker compose para el despliegue de un contenedor con un servidor FHIR Este contenedor va a necesitar un fichero de configuración, que para que funcione en la versión R5 del estándar FHIR se han tocado las siguientes opciones: spring: main: allow-bean-definition-overriding: true hapi: fhir: ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 fhir_version: R5 tester: home: fhir_version: R5 global: fhir_version: R5 Código59: Fragmento de configuración que debe estar presente en el contenedor del servidor FHIR para que sea compatible con la versión R5 de FHIR Para un entorno de producción también es posible dotar a este contenedor del uso de una base de datos postgresql, aunque por defecto utiliza una BD H2 interna en el contenedor, la cual se pierde cuando se borra el contenedor. La primera vez que se utilice será necesario publicar el recurso de tipo Questionnaire que se utiliza en la aplicación. Bastará con tenerlo definido en un fichero questionnaire_fhir.json y ejecutar el siguiente comando: curl -X POST -H 'Content-Type: application/json' -d @questionnaire_fhir.json http:// localhost:8080/fhir/Questionnaire Código60: Comando para la publicación de un recurso Questionnaire a través de la API REST del servidor FHIR 78
Apéndice B Patrones de diseño En este apéndice se van a presentar algunos patrones de diseño que se han tomado como referencia a la hora de implementar la aplicación web. B.1 Arquitectura MVC El patrón MVC (Modelo, Vista, Controlador) tiene como objetivo separar la aplicación en 3 secciones, cada una con sus responsabilidades, de forma que la lógica de la aplicación (Controlador), queda separada de la lógica de los datos (Modelo), y de la presentación de datos (Vista) [50]. Es un patrón muy común en las aplicaciones Web. • Modelo: Se encarga de toda la gestión (todas las operaciones CRUD, creación, lectura, modificación y eliminación) y representación de datos. Es el componente encargado de generar lo que se va a proporcionar al cliente, a través de la información que le pase el controlador. • Vista: Es el componente que va a representar lo que el modelo ha generado para proporcionar al cliente; se puede interpretar como plantillas que generan contenido a partir de información. Ese contenido podrá ser HTML, JSON, …, y siempre es presentado al cliente por el controlador. • Controlador: Es el componente que contiene la lógica de funcionamiento de la aplicación (hilo de ejecución, gestión ante fallos, etc). Controla el flujo de trabajo de toda la aplicación. Utiliza tanto la vista como el modelo para funcionar, sin tener todas las capacidades y responsabilidades. B.1.1 Ejemplo web Cuando se accede a una página web, a través de una URL, el usuario está realizando una petición, que será procesada por el controlador correspondiente (según la URL); ese controlador, recibirá toda la información pertinente de la petición. Para las peticiones más simples, el controlador, lanzará llamadas al modelo para que este le proporcione lo que se está solicitando en esa petición, y una vez lo tiene, se lo proporciona a la Vista para que le indique el contenido final que le va a presentar al cliente, como respuesta a la petición que ha realizado. En flujos de trabajos más complejos, esas llamadas al modelo serán parte de la lógica que ejecutan los controladores para poder responder a las peticiones. B.1.2 Beneficios e inconvenientes • Se lleva utilizando durante mucho tiempo, y es algo muy asentado. • Se enseña habitualmente. • Supone una introducción a toda la lógica de separación de capas y responsabilidades. 79
Capítulo B Patrones de diseño • En determinados escenarios puede presentar un problema de escalabilidad. Por ejemplo, si la complejidad de la aplicación crece mucho puede complicarse mantener la separación de responsabilidades. • Separación de responsabilidades: La división de la aplicación en 3 secciones, permite la evolución y desarrollo en paralelo de cada una de ellas, simplificando todo tipo de cambios. • Reutilización de código: Cada una de las capas (Modelo, Vista, Controlador) pueden ser utilizadas en otros proyectos, de manera totalmente individual, sin depender del resto de capas. B.2 Patrón Factory El patrón de diseño Factory establece que la creación de objetos se realice a partir de una clase Factory [51]. Entre los beneficios que aporta este planteamiento: • Permite el uso de distintas instancias según la configuración • Permite delegar toda la lógica de creación de objetos a la clase Factory • Resulta útil cuando se está diseñando y se desconoce el subtipo de objeto concreto que se va a utilizar B.3 Patrón Abstract Factory El patrón de diseño Abstract Factory establece un enfoque de desarrollo en el que se busca agrupar una familia, un grupo de clases que tienen un funcionamiento común, que son generadas por un Factory, con la idea de que cada familia sea una implementación diferente de una solución que resuelve un problema [52]. B.4 Clases de modelo Las clases modelo son una representación de los datos y la lógica de negocio en una aplicación. Se utilizan para gestionar y manipular la información que maneja la aplicación. Dentro de las clases modelo, podemos distinguir tipos de clases según su funcionalidad (como las clases entidad y DTO) y su estructura (como POJO y JavaBean). B.4.1 POJO Un POJO (Plain Old Java Object) es una clase Java que no depende de ningún framework determinado, por lo que cualquier programa Java, que utilice cualquier framework puede utilizarla [53], [54]. Su uso se da principalmente para la definición de entidades de la aplicación. El principal beneficio de este tipo de clases es la flexibilidad y reutilización que tienen. Sin embargo, lo que caracteriza a las clases POJO también trae consigo limitaciones, puesto que la falta de acuerdo en cómo utilizarla hace que sea necesario aprender a utilizar cada implementación, además de limitar la eficacia de utilizar un framework (los cuales tratan de promover una convención de uso que va por encima de la configuración) y de complicar la posibilidad de aumentar la funcionalidad de la clase. Como solución a esa limitación nació el concepto de JavaBean que se presenta en la siguiente sección. 80
B.4.2 JavaBean Los JavaBean son la evolución de POJO, clases definidas fuera de cualquier framework java, pero que además siguen una serie de convenciones de uso particulares, un acuerdo de uso e implementación que reduce considerablemente la curva de aprendizaje de estas. De esta forma, todos los JavaBean son POJO, pero los POJO no son JavaBean [55], [56]. Entre esas convecciones de acuerdo se encuentran: • Existencia de un constructor sin argumentos • Atributos privados • Métodos para obtener y definir los atributos privados (getAtributo, isAtributo and setAtributo), • Es un objeto serializable que puede ser almacenado por la aplicación Sin embargo, el seguir al pie de la letra estos acuerdos de uso no siempre trae beneficios, resultando en código extra innecesario. B.4.3 Entidad Una clase entidad es una clase definida para mapear un elemento que viene de una fuente de datos, una representación del tipo de elemento que se persiste. Las clases entidad son al final una clase que tiene este uso determinado, entrando dentro de la categoría de las clases POJO, y en caso de seguir las convenciones que pueden seguir los Bean, también serán clases JavaBean. Para que las entidades se utilicen correctamente y no se adulteren, existe el patrón DTO. B.4.4 DTO (Data Transfer Object) El patrón DTO ofrece un enfoque de diseño para establecer cómo deben viajar los datos, las entidades de la aplicación, entre las distintas capas o módulos que forman una aplicación [57], [58]. Este tipo de clases se caracterizan por no contener lógica de negocio. El principal beneficio que aporta este patrón arquitectónico es evitar que la aplicación dependa estrictamente del modelo de datos (las clases entidad) lo cual se hace posible al trabajar con estructuras de datos independientes al modelo de datos. Esto permite que cuando sea necesario trabajar con información que no se corresponde con una sola entidad, se puedan pasar ese conjunto de datos entre capas, sin necesidad de alterar las clases entidad, evitando que contenga más información que la que tiene la entidad del modelo de datos a la que representa. B.5 Patrón DAO El patrón arquitectónico DAO (Data Access Object), establece un enfoque de desarrollo en el que se busca aislar la lógica de acceso o persistencia de los datos de toda la lógica de negocio de la aplicación (lógica que dicta el comportamiento que debe tener la aplicación) [59]. Dicho esto, lo que se hace es definir una clase que empleará métodos de persistencia determinados para los objetos de la entidad con la que trabaja, de forma que ofrecerá operaciones CRUD (crear, leer, actualizar y eliminar). Es el intermediario entre el sistema de persistencia y la aplicación. Este planteamiento ofrece como beneficios: • Posibilidad de modificar la fuente de datos sin tener que modificar la lógica de negocio, al quedar totalmente separadas. 81
Capítulo C Implementaciones de consultas fallidas private boolean processInstanceHasTaskWithNameStatus( Long processInstanceId, String taskName, String taskStatus){ boolean result = false; Iterator <Long> taskIdList = runtimeDataService.getTasksByProcessInstanceId( processInstanceId).iterator(); Long taskId = null; // Se busca una tarea de la Instancia de proceso indicada, y que además cumpla con lo siguiente while(result == false && taskIdList.hasNext()){ taskId = taskIdList.next(); UserTaskInstanceDesc userTask = runtimeDataService.getTaskById(taskId); if (userTask.getName().equals(taskName)){ //userTaskService.getTask(taskId).getName().equals(taskName) // Se busca una tarea con el nombre indicado en taskId if (taskStatus.equals("Completed") && userTask.getStatus().equals("Completed")){ // Se busca una tarea que ha terminado result = true; }else if ( taskStatus.equals("NotCompleted") && ( runtimeDataService.getTaskById(taskId).getStatus().equals("Reserved") || runtimeDataService.getTaskById(taskId).getStatus().equals("InProgress") ) ){ // Se busca una tarea que no ha terminado result = true; } } } return result; } Código68: Función que comprueba que una instancia de proceso tiene una tarea de tipo revisión de solicitud de consentimiento, en un estado determinado. En esta implementación se manejan los estados de las tareas en vez de los de las instancias de proceso, dado que sí que están documentados y se pueden utilizar con total seguridad. Sin embargo, aparece un nuevo obstáculo, y es que el método del servicio RuntimeDataService que obtiene las variables de un proceso las devuelve de forma que cuando esa variable proviene del proceso padre (cuando se definen los procesos se hacen mapeos de las variables de un proceso padre a un proceso hijo, y lo mismo en las tareas), el nombre no es el definido, si no que se tiene un nombre que referencia al mapeo. El siguiente fragmento de código es el que se implementó y que presenta el problema expuesto: 88
VariableDesc varDesc = null; Iterator<VariableDesc> iterator = null; iterator = runtimeDataService.getVariablesCurrentState( processInstanceParentId).iterator(); while (iterator.hasNext()){ varDesc = iterator.next(); logger.info(varDesc.toString()); switch(varDesc.getVariableId()) { case "fhirServer": fhirServer = varDesc.getNewValue(); break; case "requestQuestionnaireId": requestQuestionnaireId = varDesc.getNewValue(); break; case "requestQuestionnaireResponseId": requestQuestionnaireResponseId = varDesc.getNewValue(); break; case "patient": patient = varDesc.getNewValue(); break; case "reviewQuestionnaireId": reviewQuestionnaireId = varDesc.getNewValue(); break; case "reviewQuestionnaireResponseId": reviewQuestionnaireResponseId = varDesc.getNewValue(); break; case "review": review = Boolean.parseBoolean(varDesc.getNewValue()); break; default: break; } } Código69: Fragmento de código que se encarga de obtener las variables de una instancia de proceso Este problema puede ser sorteado recogiendo de manera manual el identificador de cada variable, pero además de ser engorroso y poco escalable, si la API no ofrece esa información así, es por alguna razón de diseño, por lo que se procede a buscar un servicio alternativo, pasando a probar una nueva implementación con el servicio QueryService . C.3 Servicio de consultas El servicio QueryService es el más adecuado para lo que se quiere obtener, una consulta compleja con una serie de restricciones en varios atributos de diferentes entidades. La razón por la que este servicio es tan potente, es porque es un mecanismo para configurar consultas SQL a la base de datos del motor de procesos. Este proceso puede ser utilizado, sin embargo presenta una alta complejidad, y tras algunos intentos fallidos al tratar de utilizar distintas consultas SQL, y por el desconocimiento del modelo de datos interno del motor de procesos, se desechó su uso al encontrar el servicio AdvancedRuntimeDataService , que es el que se utiliza en la implementación final de manera totalmente funcional. 89
Capítulo C Implementaciones de consultas fallidas En el siguiente fragmento de código se puede ver un intento de alcanzar lo que se buscaba: public List <ReviewedConsent> getConsentsByPractitioner(String practitioner){ List <ReviewedConsent> consentsList = new ArrayList<ReviewedConsent>(); SqlQueryDefinition queryProcessInstances = null; List<ProcessInstanceDesc> processInstanceParentLogs = null; final List<String> processInstanceIdRequestList = new ArrayList<String>(); List<ProcessInstanceDesc> processInstanceLogs = null; final List<String> processInstanceIdList = new ArrayList<String>(); SqlQueryDefinition queryTasks = null; List<UserTaskInstanceDesc> taskInstanceLogs = null; SqlQueryDefinition queryProcessInstanceWithVars = null; Map<String, String> variableMap = null; // // Definición y registro de las consultas SQL que se quieren realizar a la BD // queryProcessInstances = new SqlQueryDefinition( "getAllProcessInstances", dataSource); queryProcessInstances.setExpression("select * from processinstancelog"); try { queryService.registerQuery(queryProcessInstances); } catch (Exception e) { // handle exception } queryTasks = new SqlQueryDefinition( "getAllTaskInputInstancesWithVariables", dataSource); queryTasks.setExpression( "select DISTINCT ti.*, tv.name tvname, tv.value tvvalue "+ "from AuditTaskImpl ti " + "inner join (select tv.taskId, tv.name, tv.value from TaskVariableImpl tv where tv.type = 0 ) tv "+ "on (tv.taskId = ti.taskId)"); //queryTasks.setExpression( //"select DISTINCT ti.*, tv.name tvname, tv.value tvvalue "+ //"from AuditTaskImpl ti " + //"inner join (select tv.taskId, tv.name, tv.value from TaskVariableImpl tv where tv.type = 0 ) tv "+ //"on (tv.taskId = ti.taskId)"); try { queryService.registerQuery(queryTasks); } catch (Exception e) { // handle exception } queryProcessInstanceWithVars = new SqlQueryDefinition( "AllProcessInstancesWithVariables", dataSource); queryProcessInstanceWithVars.setExpression("select * " + 90
"from ProcessInstanceLog pil " + "INNER JOIN (select vil.processInstanceId, MAX(vil.ID) maxvilid FROM VariableInstanceLog vil) x " + "ON (v.id = x.maxvilid )" + "INNER JOIN VariableInstanceLog v " + "ON (v.processInstanceId = pil.processInstanceId)"); try { queryService.registerQuery(queryProcessInstanceWithVars); } catch (Exception e) { // handle exception } /* La tabla VariableInstanceLog es básicamente una lista con las distintas variables que tiene cada una de las instancias de proceso, el caso es que estas variables están repetidas, no tienen un único valor, puesto que esta tabla actúa como historial, de forma que cada variable está identificada por un ID que va incrementando, por lo que para obtener el último valor disponible de una variable, para una instancia de proceso determinada es necesario filtrar las columnas para el mayor de los ID de esas filas. */ // // Ejecución de las distintas peticiones a la BD // // Lista con los ID de instancias de procesos de "ConsentRequest" pertenecientes a <practitioner> processInstanceParentLogs = queryService.query( queryProcessInstances.getName(), ProcessInstanceQueryMapper.get(), new QueryContext(), QueryParam.equalsTo( QueryResultMapper.COLUMN_PROCESSID, processConsentRequestId)); //QueryParam.equalsTo(QueryResultMapper.COLUMN_TASK_VAR_NAME, "practitioner"), //QueryParam.equalsTo(QueryResultMapper.COLUMN_TASK_VAR_VALUE, practitioner)); //QueryParam.equalsTo("practitioner", practitioner)); logger.info(processInstanceParentLogs); for (ProcessInstanceDesc pi:processInstanceParentLogs){ processInstanceIdRequestList.add(pi.getId().toString()); } // Lista con los ID de instancias de procesos de "ConsentReview" cuyo padre pertenece a <practitioner> processInstanceLogs = queryService.query( queryProcessInstances.getName(), ProcessInstanceQueryMapper.get(), new QueryContext(), QueryParam.equalsTo( QueryResultMapper.COLUMN_PROCESSID, processConsentReviewId), QueryParam.in( QueryResultMapper.COLUMN_PARENTPROCESSINSTANCEID, processInstanceIdRequestList)); logger.info(processInstanceLogs); 91
Capítulo C Implementaciones de consultas fallidas for (ProcessInstanceDesc pi:processInstanceLogs){ processInstanceIdList.add(pi.getId().toString()); } // Lista de Tareas de revisión de consentimiento, que pertenecen al proceso "ConsentReview", y que cuyo padre de la instancia a la que pertenece es creado por <practitioner> variableMap = new HashMap<String, String>(); variableMap.put("fhirServer","string"); variableMap.put("requestQuestionnaireId","string"); variableMap.put("requestQuestionnaireResponseId","string"); variableMap.put("patient","string"); variableMap.put("reviewQuestionnaireId","string"); variableMap.put("reviewQuestionnaireResponseId","string"); variableMap.put("review","boolean"); taskInstanceLogs = queryService.query(queryTasks.getName(), UserTaskInstanceQueryMapper.get(), new QueryContext(), QueryParam.equalsTo( QueryResultMapper.COLUMN_NAME, reviewHumanTaskName), QueryParam.equalsTo( QueryResultMapper.COLUMN_TASK_PROCESSID, processConsentReviewId), QueryParam.equalsTo( QueryResultMapper.COLUMN_TASK_STATUS, Status.Completed.toString()), QueryParam.in( QueryResultMapper.COLUMN_PROCESSINSTANCEID, processInstanceIdList)); logger.info(taskInstanceLogs); List<ProcessInstanceWithVarsDesc> piDescList = null; Map<String,Object> piVars = null; logger.info(piDescList); for (UserTaskInstanceDesc userTask :taskInstanceLogs){ piDescList = advancedRuntimeDataService.queryProcessByVariables( new ArrayList<QueryParam>(){{ add(QueryParam.equalsTo( QueryResultMapper.COLUMN_PROCESSID, processConsentReviewId)); add(QueryParam.equalsTo( QueryResultMapper.COLUMN_PROCESSINSTANCEID, userTask.getProcessInstanceId().toString())); }}, null, new QueryContext()); //piDescList = queryService.query(queryProcessInstanceWithVars.getName(), // ProcessInstanceWithVarsQueryMapper.get(), // new QueryContext(), // QueryParam.equalsTo( // QueryResultMapper.COLUMN_PROCESSID, processConsentReviewId), // QueryParam.equalsTo( // QueryResultMapper.COLUMN_PROCESSINSTANCEID, // userTask.getProcessInstanceId().toString())); 92
logger.info(piDescList); piVars = piDescList.get(0).getVariables(); consentsList.add(new ReviewedConsent( (String) piVars.get("fhirServer"), (String) piVars.get("requestQuestionnaireId"), (String) piVars.get("requestQuestionnaireResponseId"), (String) piVars.get("practitioner"), (String) piVars.get("patient"), (String) piVars.get("reviewQuestionnaireId"), (String) piVars.get("reviewQuestionnaireResponseId"), (Boolean) piVars.get("review"))); } return consentsList; } Código70: Fragmento de código que hace uso del servicio “QueryService” para obtener los consentimientos que ha obtenido un facultativo 93
Capítulo C Implementaciones de consultas fallidas 94
Bibliografía [1] T. jBPM Team, «jBPM Documentation». [En línea]. Disponible en: https://docs.jbpm.org/7.74.1. Final/jbpm-docs/html_single/ [2] HL7, «Introducing HL7 FHIR». [En línea]. Disponible en: https://hl7.org/fhir/summary.html [3] Kie, «jBPM Services API». [En línea]. Disponible en: https://javadoc.io/doc/org.jbpm/jbpmservices-api/latest/index.html [4] mswiderski, «Advanced queries in jBPM 6.4». [En línea]. Disponible en: https://mswiderski. blogspot.com/2016/01/advanced-queries-in-jbpm-64.html [5] Ibm, «Aplicaciones empresariales». [En línea]. Disponible en: https://www.gartner.com/en/ information-technology/glossary/bam-business-activity-monitoring [6] Gartner, «Business Activity Monitoring (BAM)». [En línea]. Disponible en: https://www.gartner. com/en/information-technology/glossary/bam-business-activity-monitoring [7] R. Hat, «¿Qué es la gestión de los procesos empresariales?». [En línea]. Disponible en: https:// www.redhat.com/es/topics/automation/what-is-business-process-management [8] Wikipedia, «Business process». [En línea]. Disponible en: https://en.wikipedia.org/wiki/ Business_process [9] A. Team, «Qué es un sistema de gestión por procesos (BPM)». [En línea]. Disponible en: https:// www.ambit-bst.com/blog/qu%C3%A9-es-un-sistema-de-gesti%C3%B3n-por-procesos-bpm [10] A. Rodríguez, «BPM y Java Open Source». [En línea]. Disponible en: http://aitorrm.github.io/t% C3%A9cnicas%20y%20metodolog%C3%ADas/bpm_y_java_open_source/ [11] Wikipedia, «Gestión de procesos de negocio». [En línea]. Disponible en: https://es.wikipedia. org/wiki/Gesti%C3%B3n_de_procesos_de_negocio [12] Sydle, «BPM, BPMN y BPMS: ¿Qué significan? Comprende cada término». [En línea]. Disponible en: https://www.sydle.com/es/blog/bpm-bpmn-bpms-60ba98c3a5c829237349b32f/ [13] Deloitte, «FHIR, ese gran desconocido». [En línea]. Disponible en: https://www2.deloitte.com/ es/es/pages/technology/articles/fhir-ese-gran-desconocido.html [14] F. Brutti, «¿Qué es Spring Boot y en qué se diferencia de Spring Framework?». [En línea]. Disponible en: https://thepower.education/blog/que-es-spring-boot [15] S. D. Health, «HAPI FHIR». [En línea]. Disponible en: https://hapifhir.io/ 95
[16] S. Davis, «La diferencia entre JDBC, JPA, Hibernate y Spring Data JPA». [En línea]. Disponible en: https://hackernoon.com/es/la-diferencia-entre-jdbc-jpa-hibernate-y-spring-data-jpa [17] Kie, «Kie Community». [En línea]. Disponible en: https://www.kie.org/about/ [18] T. A. S. Foundation, «Maven». [En línea]. Disponible en: https://maven.apache.org/ [19] L. Crusoveanu, «A Comparison Between Spring and Spring Boot». [En línea]. Disponible en: https://www.baeldung.com/spring-vs-spring-boot [20] D. Valverde, «¿Qué es Spring?». [En línea]. Disponible en: https://www.davidvalverde.com/blog/ que-es-spring/ [21] V. Tanzu, «Spring Framework». [En línea]. Disponible en: https://spring.io/projects/springframework [22] V. Tanzu, «Spring Boot». [En línea]. Disponible en: https://spring.io/projects/spring-boot [23] A. Team, «¿Qué es JBPM?». [En línea]. Disponible en: https://www.ambit-bst.com/blog/qu%C3% A9-es-jbpm-tutorial [24] Kie, «Java BPM». [En línea]. Disponible en: https://www.jbpm.org/ [25] J. A. G. Linares, «Github GestorConsentimientos». [En línea]. Disponible en: https://github.com/ josgarlin/GestorConsentimientos [26] R. Arnau, «Tutorial de Maven». [En línea]. Disponible en: https://www.arteco-consulting.com/ post/tutorial-de-maven [27] F. Brutti, «¿Qué es Spring Boot y en qué se diferencia de Spring Framework?». [En línea]. Disponible en: https://www.thepowermba.com/es/blog/que-es-spring-boot [28] L. M. L. Magaña, «Qué es Thymeleaf». [En línea]. Disponible en: https://openwebinars.net/blog/ que-es-thymeleaf/ [29] M. Kirchmer, M. Scarsig, R. Saxena, y T. Benedict, BPM Common Body of Knowledge. Amazon Digital Services LLC - KDP Print US, 2019. [30] D. Santos, «Qué es BPM y para qué sirve: todo sobre el business process management». [En línea]. Disponible en: https://blog.hubspot.es/marketing/que-es-bpm [31] Gbtec, «¿Para que sirve el Business Process Management o BPM?». [En línea]. Disponible en: https://www.gbtec.com/es/recursos/bpm/ [32] L. Elduayen, «¿Qué es BPM? Gestión de procesos de negocio». [En línea]. Disponible en: https:// flokzu.com/es/bpm-es/que-es-bpm/ [33] Kissflow, «BPM vs Workflow: What’s the Difference?». [En línea]. Disponible en: https://kissflow. com/workflow/bpm/bpm-vs-workflow/ [34] Heflo, «BPM y workflow no es igual: entienda sus similitudes y diferencias». [En línea]. Disponible en: https://www.heflo.com/es/blog/workflow/bpm-workflow/ [35] Ultimus, «BPM vs. Workflow vs. Low-Code». [En línea]. Disponible en: https://www.ultimus. com/bpm-vs-workflow [36] Bonitasoft, «Bonitasoft». [En línea]. Disponible en: https://es.bonitasoft.com/ [37] Activiti, «Activiti». [En línea]. Disponible en: https://www.activiti.org/ [38] Camunda, «Camunda». [En línea]. Disponible en: https://camunda.com/ 96
[39] K. Varela, «Know jBPM: your open-source business automation tool». [En línea]. Disponible en: https://karinavarela.me/2020/05/03/jbpm-business-automation/ [40] Kie, «REST API». [En línea]. Disponible en: https://www.jbpm.org/api-docs/kie-server/paths. html [41] HL7, «HLTAbout HL7». [En línea]. Disponible en: https://www.hl7.org/about/index.cfm [42] S. D. Health, «HAPI FHIR Javadoc». [En línea]. Disponible en: https://javadoc.io/doc/ca.uhn.hapi. fhir/hapi-fhir-structures-r5/6.4.0/index.html [43] U. H. Network, «HAPI Test Server». [En línea]. Disponible en: http://hapi.fhir.org/ [44] Spring, «Authorize HttpServletRequests». [En línea]. Disponible en: https://docs.spring.io/ spring-security/reference/servlet/authorization/authorize-http-requests.html [45] F. J. R. Raposo, «Github GestorConsentimientos». [En línea]. Disponible en: https://github.com/ tfg-projects-dit-us/ConsentFlow-Hub [46] L. M. F. Teomiro, «Aprendiendo WSO2 Identity Server (III): Políticas XACML». [En línea]. Disponible en: https://www.enmilocalfunciona.io/aprendiendo-wso2-identity-server-iii-politicas- xacml/ [47] WSO2, «Github WSO2». [En línea]. Disponible en: https://github.com/wso2/product-is [48] G. Loganathan, «Install Oracle Java Development Kit 8 on Linux». [En línea]. Disponible en: https://javahelps.com/install-oracle-jdk-8-on-linux [49] Docker, «Get Docker». [En línea]. Disponible en: https://docs.docker.com/get-docker/ [50] V. Paradigm, «What is Model-View and Control?». [En línea]. Disponible en: https://www.visualparadigm.com/guide/uml-unified-modeling-language/what-is-model-view-control-mvc/ [51] O. Blancarte, «Factory Method». [En línea]. Disponible en: https://reactiveprogramming.io/blog/ es/patrones-de-diseno/factory-method [52] O. Blancarte, «Abstract Factory». [En línea]. Disponible en: https://reactiveprogramming.io/blog/ es/patrones-de-diseno/abstract-factory [53] A. Frieze, «What Is a Pojo Class?». [En línea]. Disponible en: https://www.baeldung.com/javapojo-class [54] S. Swiniarski, «POJO». [En línea]. Disponible en: https://www.codecademy.com/resources/docs/ java/pojo [55] S. Swiniarski, «JavaBeans». [En línea]. Disponible en: https://www.codecademy.com/resources/ docs/java/javabeans [56] V. Garg, «POJO vs Java Beans». [En línea]. Disponible en: https://www.geeksforgeeks.org/pojovs-java-beans/ [57] O. Blancarte, «Data Transfer Object (DTO)». [En línea]. Disponible en: https://www.oscarblanc arteblog.com/2018/11/30/data-transfer-object-dto-patron-diseno/ [58] O. Blancarte, «Data Transfer Object (DTO)». [En línea]. Disponible en: https://reactiveprogram ming.io/blog/es/patrones-arquitectonicos/dto [59] O. Blancarte, «Data Access Object (DAO)». [En línea]. Disponible en: https://www.oscarblancar teblog.com/2018/12/10/data-access-object-dao-pattern/ 97