El presente artículo es una continuación de los artículos Transformaciones ATL con Perfiles UML y Adaptando BindingsEjb en Aplicaciones Oracle ADF, en los cuales se exponen y detallan los siguientes elementos: un metamodelo para clases Java, un perfil UML básico para EJB y un meta-framework denominado BindingsEjb para adaptar aplicaciones Oracle ADF utilizando la herramienta EJB 3.0.
Ahora estamos en la posición de plantear un metamodelo completo de clases Java así como también del perfil UML para EJB versión 3.0 que puede ser utilizado de manera productiva en un proyecto de desarrollo de software.
Metamodelo completo de clases Java
Las principales características que tiene este metamodelo se pueden resumir de la siguiente forma.
- Elementos genéricos de Java (JavaElements).
- Clases Java (JavaClass) que tienen asociados métodos (Methods) y atributos (Fields).
- Las clases Java pertenecen a un paquete (Package).
- Modificadores Java (Modifiers) que indican si los elementos son públicos, estáticos o finales (public, static, final).
- Los métodos pueden contener un conjunto de parámetros (MethodParameter) de un tipo indicado.
- Existe un tipo de dato genérico (Type), el cual puede representar un dato primitivo de Java (por ejemplo String, Date, Integer, etc) o una clase Java, para los datos primitivos puede usarse el paquete donde esta implementado dicho dato (packageImpl).
- La clase Java (JavaClass) y la clase PrimitiveTypes son del tipo Type.
- En un paquete pueden definirse un conjunto de tipos Enumerativos Java (Enumeration) que poseen un grupo de literales (EnumerationLiteral).
- Un método tiene un cuerpo o código del metodo (body).
- Un atributo o campo de una clase, un parámetro de un método o el tipo de dato de retorno de un método, pueden definirse del tipo Type y según su cardinalidad (upperBound) puede definir una colección de datos (collectionType).
- Cualquier elemento de Java (JavaElement), puede tener asociado un conjunto de anotaciones (Annotations).
- Las anotaciones pueden definirse recursivamente (parentAnnotation) y utilizar un conjunto de atributos (AnnotationAtributte).
- Pueden definirse clases Java de tipo Generics utilizando JavaClassGeneric, en la misma se puede especificar si se usara Generics de Java (useGeneric) y también la clases genérica utilizando el atributo genericClass.
- Según las definiciones de clases genéricas en el modelo estas pueden utilizar en una clase Java, mediante el atributo superClasses.
Fig 1. Metamodelo para clases Java
Para descargar el archivo ecore del metamodelo pueden presionar aquí.
Perfil UML para EJB 3.0
Para mejorar el uso del perfil UML propuesto originalmente, se lo dividió en tres paquetes, cada uno contiene un conjunto de estereotipos necesarios para la definición de elementos y anotaciones EJB, adicionalmente se crearon estereotipos para definir elementos utilizados en los patrones de diseño Data Transfer Object (DTO) y Data Access Object (DAO) utilizados en el meta-framework BindingsEjb. A continuación describimos brevemente los estereotipos elaborados en cada paquete.
Estereotipos del paquete EJB:
- EntityEJB, permite crear la anotación @EJB para una clase Java de tipo Entity Bean.
- StatelessEJB, permite crear la anotación @Stateless para una clase Java de tipo Stateless Session Bean.
- StatefulEJB, permite crear la anotación @Stateful para una clase Java de tipo Stateful Session Bean.
- MessageEJB, permite crear la anotación @Message para una clase Java de tipo Message Driven Bean.
- IdClassEJB, permite crear la anotación @IdClass a una clase Java de tipo Entity Bean.
- TableEJB, permite crear la anotación @Table a una clase Java de tipo Entity Bean.
- Remote Bussines Interface, permite crear la anotación @Remote a una Interface Java de tipo Remoto.
- Local Bussines Interface, permite crear la anotación @Local a una Interface Java de tipo Local.
- Message Listener Interface, permite crear la anotación @Message a una Interface Java para Mensajeria.
- PrimaryKeyField, permite crear la anotación @Id a un atributo de una clase Java de tipo Entity Bean que se considera como parte de la llave primaria.
- ColumnField, permite crear la anotación @Column a un atributo de una clase Java de tipo Entity Bean. Debe considerarse una serie de atributos (tag values) disponibles en el estereotipo que permiten definir la columna de forma completa.
- TransientField, permite crear la anotación @Transient a un atributo de una clase Java de tipo Entity Bean que se considere como de tipo Transient o no persistible.
- PersistenceContextField, permite crear la anotación @PersistentConext a un atributo de una clase Java que se considere pueda instanciar la clase EntityManager.
- GeneratedValueField, permite crear la anotación @GeneratedValue a un atributo de una clase Java de tipo Entity Bean que se considera como parte de la llave primaria y el Contenedor EJB genere el valor de la llave primaria para la clase.
- OneToOneRelation, permite crear la anotación @OneToOne a un atributo de una clase Java de tipo Entity Bean, este atributo se creara automáticamente por medio de una asociación entre dos clases Java con cardinalidad 1 – 1 (uno a uno).
- OneToManyRelation, permite crear la anotación @OneToMany a un atributo de una clase Java de tipo Entity Bean, este atributo se creara automáticamente por medio de una asociación entre dos clases Java con cardinalidad 1 – * (uno a muchos).
- PrePersistMethod, permite crear la anotación @PrePersist a un método de una clase Java de tipo Entity Bean que se considera como método callback a ejecutarse antes de persistir el Entity.
- PostPersistMethod, permite crear la anotación @PostPersist a un método de una clase Java de tipo Entity Bean que se considera como método callback a ejecutarse después de persistir el Entity.
- PreUpdateMethod, permite crear la anotación @PreUpdate a un método de una clase Java de tipo Entity Bean que se considera como método callback a ejecutarse antes de actualizar el Entity.
- PostUpdateMethod, permite crear la anotación @PostUpdate a un método de una clase Java de tipo Entity Bean que se considera como método callback a ejecutarse después de actualizar el Entity.
- PreDeleteMethod, permite crear la anotación @PreDelete a un método de una clase Java de tipo Entity Bean que se considera como método callback a ejecutarse antes de eliminar el Entity.
- PostDeleteMethod, permite crear la anotación @PostDelete a un método de una clase Java de tipo Entity Bean que se considera como método callback a ejecutarse después de eliminar el Entity.
- PostLoadMethod, permite crear la anotación @PostLoad a un método de una clase Java de tipo Entity Bean que se considera como método callback a ejecutarse después de recuperar el Entity.
- PostConstructMethod, permite crear la anotación @PostConstruct a un método de una clase Java de tipo Session Bean que se considera como método callback a ejecutarse después de construirse el Session Bean.
- PreDestroyMethod, permite crear la anotación @PreDestroy a un método de una clase Java de tipo Stateful Session Bean que se considera como método callback a ejecutarse antes de destruirse el Session Bean.
- PostActivateMethod, permite crear la anotación @PostActivate a un método de una clase Java de tipo Stateful Session Bean que se considera como método callback a ejecutarse despues de activarse el Session Bean.
- PrePassivateMethod, permite crear la anotación @PrePassivate a un método de una clase Java de tipo Stateful Session Bean que se considera como método callback a ejecutarse antes de inactivarse o colocarse en estado pasivo el Session Bean.
La siguiente figura ilustra el paquete EJB del perfil:
Fig 2. Estereotipos del Paquete EJB
Estereotipos del paquete DTO:
- DataTransferObject, utilizado para definir una clase Java como un DTO, en estos casos será necesario crear una relación de Dependencia entre la clase EntityBean y la clase Java para que el programa de transformación pueda crear los métodos necesarios para implementar este patrón de diseño tanto en el EntityBean como en la Clase DTO.
- CreateTransferObject y MergeTransferObject, estos estereotipos son solo referenciales por el momento no tiene una función específica en el proceso de transformación.
La siguiente figura ilustra el paquete DTO y sus estereotipos:
Fig 3. Estereotipos del Paquete DTO
Estereotipos del paquete DAO:
- DataAccessObject, utilizado para definir una clase Java como un DAO, en estos casos será necesario crear una relación de Dependencia entre la clase EntityBean y la clase Java para que el programa de transformación pueda crear los métodos necesarios para implementar este patrón de diseño en la Clase DAO. Adicionalmente, todas las clases DAO deberán generalizarse de clase GenericDAO que implementen bindeos de Templetas UML con el fin de implementar clases Java “Genericas”, esto según la definición de la clase GenericDAO
del meta-framework BindingsEjb. Como referencia de Templetas UML revise el artículo “Defining Generics with UML Templates”. - DeleteMethod, este estereotipo es solo referencial por el momento no tiene una función específica en el proceso de transformación.
La siguiente figura ilustra el paquete DAO y sus estereotipos:
Fig 4. Estereotipos del Paquete DAO
Para descargar el Perfil EJB completo puede hacerlo de aquí.
Transformación UML2Java
La transformación fue completamente modificada de la original (ver artículo Transformaciones ATL con Perfiles UML), la misma ahora soporta todos los estereotipos del Perfil EJB, algunas características que se añadieron son las siguientes:
Se añadieron métodos helpers que permiten realizar consultas a los elementos de UML así como variables auxiliares para apoyar durante la transformación, como por ejemplo:
- Definir variables auxiliares para almacenar tipos de datos primitivos Java, tipos de Colección de Datos, tipos de Persistencia EJB y Clases Java transformadas.
- helper def : primitiveTypesMap : Map(String,Java!PrimitiveType) = Map{};
- helper def : collectionsTypesMap : Map(String,Java!Type) = Map{};
- helper def : persistenceTypesMap : Map(String,Java!Type) = Map{};
- helper def : classMap : Map(String,Java!JavaClass) = Map{};
- Determinar tipos de asociación entre clases UML (relación entre una clase y otra)
- helper context UML!Association def: isBinary() : Boolean
- helper context UML!Property def: isSymmetric() : Boolean
- helper context UML!Property def: isInverseFunctional() : Boolean
- Convertir el método de una operación UML a string.
- helper context UML!Operation def: methodToString() : String
- Crear métodos para implementar el patrón de diseño DTO.
- helper context UML!Class def : createTransferObject() : String
- helper context UML!Class def : mergeTransferObject() : String
Se modificaron las reglas de emparejamiento con las siguientes funcionalidades adicionales:
- Todas las reglas de emparejamiento crean anotaciones Java, recorriendo todos los estereotipos definidos en el elemento transformado utilizando la regla createStereotype(e,g)
- La regla de emparejamiento que convierte un atributo UML (UML!Property) a un campo Java (Java!Field), se divide en tres:
- A2F_PrivateAttribute, esta regla convierte solo atributos privados y si la cardinalidad del atributo es mayor a cero o menos uno (-1) al campo Java se le asocia una colección de datos (List).
- A2F_ReadOnlyPublicAttribute, esta regla convierte atributos públicos de solo lectura a un campo Java privado, con un método getter y al igual que el anterior caso se le asocia una colección de datos si fuera necesaria.
- A2F_ReadWritePublicAttribute, esta regla convierte atributos públicos de lectura y escritura a un campo Java privado, con un método getter y un setter y además de asociar una colección de datos si fuera necesario.
- A2F_Association, esta regla convierte un atributo derivado de una asociación UML entre dos clases a un campo Java privado, adicionalmente se crean métodos getter y setter para dicho atributo y se asocia una colección de datos si fuera necesario.
- La regla de emparejamiento que permite convertir una operación UML (UML!Operation) a un método Java (Java!Method) es muy similar a la definida originalmente.
- Las reglas de emparejamiento para implementar los patrones de diseño DTO y DAO son las siguientes:
- Dependency2MethodDTO, esta regla convierte una dependencia UML (UML!Dependency) y que además tenga asociado la clase dependiente el estereotipo DataTransferObject a los métodos getTransferObject, createTransferObject, setTransferObject y mergeTransferObject, además para cada método se define código Java por defecto.
- Dependency2MethodDAO, esta regla convierte una dependencia UML (UML!Dependency) y que además tenga asociado la clase dependiente el estereotipo DataAccessObject a un método Constructor de la clase Java además al método se añade código Java por defecto, también se crea un campo Java (Java!Field) que permita instanciar el objeto EntityManager de EJB, al mismo se asocian los métodos getEntityManager y setEntityManager.
Transformación Java2Code
La transformación original presentada en el artículo Transformaciones ATL con Perfiles UML, se modificó de la siguiente manera:
- Se añadió el método helper context Java!Type def : fullName() : String que permite construir de manera completa el tipo de datos de un Java!Type donde se hace referencia al atributo packageImpl.
- Se modifica el método helper context Java!JavaElement def : fullType() : String verificando la cardinalidad del elemento de Java (Java!Element) y si tiene asociado una colección de datos (atributos upperBound y collectionType), si es así al tipo de datos se le asigna la clase List
. Se modifica el método helper context Java!JavaClass def : superClassesDef() : String donde se verifica si la superclase asociada a la clase Java (Java!JavaClass) usa Generics (atributo useGeneric), si es así despliega la clase genérica entre los símbolos < y >. Se modifica el método helper context Java!Method def: toString() : String para que imprima el código Java asociado si es que lo tuviese (atributo body).
Modelo UML para transformación
Fig 5. Modelo UML para transformación.
Nótese que en las clases Persona, Usuario y Departamento, se aplicaron los estereotipos entityEJB, tableEJB y idClassEJB, para sus atributos los estereotipos primaryKeyField, columnField, en cada estereotipo se completaron sus propiedades (tagValues) apropiadamente.
Fíjese que entre la clase Usuario y Persona se creó una asociación con cardinalidad Uno a Uno denominada Usuario_tiene_persona a la cual se le aplico el estereotipo OneToOneRelation, entre las clases Departamento y Persona se creó la asociación con cardinalidad Uno a Muchos denominada Lista_empleados, a esta se aplicó el estereotipo OneToManyRelation.
Observe que para relacionar un Entity Bean con una clase DTO y DAO se crearon dependencias entre las clases del modelo, por ejemplo el Entity Bean Persona tiene como clases dependientes PersonaDTO y PersonaDAO, cabe mencionar que PersonaDAO hereda la clase GenericDAO
Finalmente se puede observar, que se crearon paquetes específicos para cada grupo de elementos comunes en el modelo, es decir por ejemplo el paquete bo.sigep.personal.entities agrupa a todas las entidades del modelo (Entity Beans).
Puede descargar el modelo “PersonasEJB.uml” de aquí.
Ejecución de las Transformaciones y serialización a código
La ejecución de las transformaciones tiene que ser en un orden específico es decir:- Ejecutar UML2Java.atl, modelo de entrada PersonasEJB.uml modelo de salida PersonasEJB.xmi.
- Luego ejecutar Java2Code.atl, modelo de entrada PersonaEJB.xmi, como salida tendríamos el modelo serializado a código Java en la carpeta “src” del proyecto Eclipse, como ejemplo se muestran algunas clases.
package bo.sigep.personal.entities; @Entity @Table(name = "PERSONAS", schema = "DBADMIN") @IdClass(PersonaPK.class) private class Persona { @Id @Column(name = "TIPO_ID") private java.lang.String tipoId; @Id @Column(name = "DOCUMENTO") private java.lang.String documento; @Id @Column(name = "EXPEDIDO") private java.lang.Integer expedido; @Column(name = "NOMBRES") private java.lang.String nombres; @Column(name = "APELLIDOS") private java.lang.String apellidos; @Column(name = "FECHA_NACIMIENTO") private java.util.Date fechaNacimiento; public java.lang.String getTipoId() { return this.tipoId; } public void setTipoId(java.lang.String tipoId) { this.tipoId = tipoId; } public java.lang.String getDocumento() { return this.documento; } public void setDocumento(java.lang.String documento) { this.documento = documento; } public java.lang.Integer getExpedido() { return this.expedido; } public void setExpedido(java.lang.Integer expedido) { this.expedido = expedido; } public java.lang.String getNombres() { return this.nombres; } public void setNombres(java.lang.String nombres) { this.nombres = nombres; } public java.lang.String getApellidos() { return this.apellidos; } public void setApellidos(java.lang.String apellidos) { this.apellidos = apellidos; } public java.util.Date getFechaNacimiento() { return this.fechaNacimiento; } public void setFechaNacimiento(java.util.Date fechaNacimiento) { this.fechaNacimiento = fechaNacimiento; } public bo.sigep.personal.dto.PersonaDTO getTransferObject() { return createTransferObject(); } private bo.sigep.personal.dto.PersonaDTO createTransferObject() { PersonaDTO dto = new PersonaDTO(); dto.setTipoId(this.tipoId); dto.setDocumento(this.documento); dto.setExpedido(this.expedido); dto.setNombres(this.nombres); dto.setApellidos(this.apellidos); dto.setFechaNacimiento(this.fechaNacimiento); return dto; } public void setTransferObject(bo.sigep.personal.dto.PersonaDTO dto) { mergeTransferObject(dto); } public void mergeTransferObject(bo.sigep.personal.dto.PersonaDTO dto) { this.setTipoId(dto.getTipoId()); this.setDocumento(dto.getDocumento()); this.setExpedido(dto.getExpedido()); this.setNombres(dto.getNombres()); this.setApellidos(dto.getApellidos()); this.setFechaNacimiento(dto.getFechaNacimiento()); } }
PersonaDTO.java
package bo.sigep.personal.dto; private class PersonaDTO { private java.lang.String tipoId; private java.lang.String documento; private java.lang.Integer expedido; private java.lang.String nombres; private java.lang.String apellidos; private java.util.Date fechaNacimiento; public java.lang.String getTipoId() { return this.tipoId; } public void setTipoId(java.lang.String tipoId) { this.tipoId = tipoId; } public java.lang.String getDocumento() { return this.documento; } public void setDocumento(java.lang.String documento) { this.documento = documento; } public java.lang.Integer getExpedido() { return this.expedido; } public void setExpedido(java.lang.Integer expedido) { this.expedido = expedido; } public java.lang.String getNombres() { return this.nombres; } public void setNombres(java.lang.String nombres) { this.nombres = nombres; } public java.lang.String getApellidos() { return this.apellidos; } public void setApellidos(java.lang.String apellidos) { this.apellidos = apellidos; } public java.util.Date getFechaNacimiento() { return this.fechaNacimiento; } public void setFechaNacimiento(java.util.Date fechaNacimiento) { this.fechaNacimiento = fechaNacimiento; } }
PersonaDAO.java
package bo.sigep.personal.dao; private class PersonaDAO extends org.mefp.core.ejb.dao.GenericDAO<bo.sigep.personal.entities.Persona> { private javax.persistence.EntityManager em; public void PersonaDAO() { super(bo.sigep.personal.dao.PersonaDAO.class); } public javax.persistence.EntityManager getEntityManager() { return this.em; } public void setEntityManager(javax.persistence.EntityManager em) { this.em = em; } }
Conclusiones
Como se podrá observar el Perfil EJB creado tiene un grado de madurez aceptable, sin embargo le faltan definir algunos detalles como por ejemplo el uso de etiquetas @JoinColumn.
En cuanto al metamodelo Java podríamos afirmar que es bueno, ahora contempla varias opciones que antes no tenía, como por ejemplo uso de Generics, uso de Colecciones de Datos, definición de código fuente, etc., pero no posee validaciones ni restricciones del modelo.
La transformación UML2Java.atl desde nuestro punto de vista está completa, le faltarían ejecutar pruebas de transformación de todo índole y con una variedad de casos mínimos y máximos, de esta manera detectar problemas con valores nulos (OclUndefined) que es el problema más frecuente.
La transformación Java2Code.atl, si bien contempla todos los casos y la generación de código es muy buena, habría que pensar en reemplazarla por un framework especializado en el manejo de templetas como XPand u otro.
El proceso de transformación y generación de código debería integrarse en un solo programa, que pueda ser ejecutado con un solo comando en el entorno Eclipse.
Todo lo presentado en el presente artículo es de manera experimental y con el afán de difusión, si existiesen nuevas ideas o propuestas, les adjunto el código para que puedan mejorar los programas de transformación elaborados.
No hay comentarios:
Publicar un comentario