JBoss RichFaces. Capítulo 4. La Aplicación.
JBoss RichFaces. Capítulo 4. La Aplicación.
JBoss RichFaces. Capítulo 4. La Aplicación.
La aplicación
En este capítulo, vamos a comenzar a desarrollar nuestra aplicación web rica!
Vamos a explicar todos los pasos para construir un proyecto vacío de la nada (con seam-gen),
que soporta todas las tecnologías que se necesitan para desarrollar una "verdadera" aplicación
JSF con Ajax.
El objetivo de este libro no sólo debe ser una descripción de una tecnología, sino también
queremos que el lector aprenda sobre el marco RichFaces, mientras que desarrolla de una
aplicación real, de modo que puede estar listo para desarrollar su propia aplicación.
La aplicación que hemos elegido es un administrador de contactos avanzada con una gran
cantidad de "ricas" características que veremos en la siguiente sección.
Después de crear el proyecto, vamos a empezar a buscar en las páginas y entender las
páginas básica generadas así como el formulario de entrada, mientras que vamos haciendo
algunas modificaciones en ellos.
La siguiente imagen muestra una maqueta simple de la aplicación que queremos desarrollar:
Características
Aquí está la descripción de las características principales de la aplicación Administrador
Avanzado de Contactos.
Administrador de Grupos
Cada contacto se puede poner en uno o más grupos para una mejor organización de sus
datos. La lista de grupos de contacto que aparece dentro de una lista de colores con una
herramienta de ventana de información emergente que muestra la descripción del grupo.
Búsqueda simple
Una simple búsqueda siempre estará disponible para el usuario. Él o ella será capaz de buscar
contactos, filtrar éstos por su nombre y apellido.
Multilenguaje
Nuestra aplicación es real. Por lo tanto, aunque no tenga características de RichFaces (pero sí
características JSF/Seam), para dar soporte a diferentes idiomas.
La base de datos: diagrama E-R
Este es el diagrama ER (Entidad-Relación) de la base de datos que vamos a utilizar:
Una entidad es un objeto (por ejemplo, una persona o un grupo) que pueden ser identificados
de forma unívoca y es independiente (es decir, que pueden "vivir" solas).
Una relación se describe cómo dos o más entidades relacionadas entre sí.
Tanto las entidades y las relaciones pueden tener atributos. Para encontrar más acerca de los
diagramas ER, se puede leer más en http://en.wikipedia.org/wiki/Entityrelationship_model.
Como puede ver, es bastante simple, todo está en torno a la entidad de contacto. Cada
contacto puede ser también un usuario de la aplicación web (con un nombre de usuario y una
contraseña establecida). Además, los demás campos se puede asociar con un tipo diferente de
campo personalizado (como dirección URL, correo electrónico, etc) y una etiqueta (casa,
trabajo, etc, dependiendo del tipo). Un contacto puede tener una o más direcciones asociadas
con una etiqueta (como el hogar, el trabajo, y así sucesivamente).
Los usuarios pueden crear uno o más grupos de contactos con un color diferente para cada
uno. Por último, una lista de archivos (como un CV o de otro tipo) se puede asociar a cada
contacto.
La base de datos está lista, podemos pasar a crear la estructura del proyecto y la API de
persistencia Java (JPA) con entidades vinculadas a las tablas de base de datos.
¿ Qué es la JPA ?
Hemos hablado acerca de JBoss Seam y Facelets, pero esta es la primera vez que hemos
hablado sobre JPA. La JPA es el acrónimo de Java Persistence API y es el estándar API de
persistencia y mapeo objeto/relacional para la plataforma Java (SE y EE).
Este es otro de los diversos conocimientos de tecnología. Es muy útil para administrar
(consultar, insertar, actualizar) datos persistentes conectado a un DBMS de una forma sencilla.
El concepto principal es la entidad, que es un POJO que normalmente representa una tabla en
una base de datos, y cada instancia de la misma representa una fila individual de la tabla.
Para dar un ejemplo, si desea insertar una nueva fila en una tabla de base de datos, sólo tienes
que crear una instancia de la clase de entidad que representa la tabla, a continuación, rellene
los campos de la entidad con los datos, y llamar al método persistente para insertar una nueva
fila en la base de datos. Usted no tiene que escribir una sola línea de SQL (JPA va a hacer ese
trabajo por usted). Además, cuando se consulta la base de datos, se utiliza un lenguaje similar
al SQL (llamado Java Persistence Query Language (JPQL)) y el resultado será una lista de
objetos (java.util.list), estos encajan perfectamente en una aplicación JSF.
La conexión con las entidades de las tablas es muy sencilla, utilizando anotaciones (dentro de
la propia clase) o los archivos descriptor XML.
Hay algunas buenas implementaciones de la JPA. Vamos a utilizar una de JBoss, puede que
usted conozca: Hibernate. Fue un marco ORM antes de la existencia de la JPA (que tiene
muchas ideas de él) y está incluido en el proyecto que la seam-gen genera para nosotros.
-- -----------------------------------------------------
-- Table `advcm_db`.`contact_address`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `advcm_db`.`contact_address` (
`id` INT(11) NOT NULL AUTO_INCREMENT ,
`ContactId` INT(11) NOT NULL ,
`position` INT(11) NOT NULL DEFAULT '0' ,
`address1` VARCHAR(255) NULL DEFAULT NULL ,
`address2` VARCHAR(255) NULL DEFAULT NULL ,
`postCode` VARCHAR(50) NULL DEFAULT NULL ,
`city` VARCHAR(255) NULL DEFAULT NULL ,
`province` VARCHAR(50) NULL DEFAULT NULL ,
`country` VARCHAR(255) NULL DEFAULT NULL ,
`label` VARCHAR(255) NOT NULL COMMENT 'it is suggested but can be a free text' ,
`createdOn` DATETIME NOT NULL ,
`lastUpdatedOn` DATETIME NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_ContactAddress_contact` (`ContactId` ASC) ,
INDEX `FK20846D5EA68B411` (`ContactId` ASC) ,
CONSTRAINT `FK20846D5EA68B411`
FOREIGN KEY (`ContactId` )
REFERENCES `advcm_db`.`contact` (`id` ),
CONSTRAINT `fk_ContactAddress_contact`
FOREIGN KEY (`ContactId` )
REFERENCES `advcm_db`.`contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `advcm_db`.`contact_field`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `advcm_db`.`contact_field` (
`id` INT(11) NOT NULL AUTO_INCREMENT ,
`ContactId` INT(11) NOT NULL ,
`label` VARCHAR(255) NOT NULL COMMENT 'the suggestions on it dipend on type but it
is a free field' ,
`value` VARCHAR(255) NOT NULL ,
`type` VARCHAR(255) NOT NULL COMMENT 'phone, url, email, etc.' ,
PRIMARY KEY (`id`) ,
INDEX `fk_ContactField_contact` (`ContactId` ASC) ,
INDEX `FK387E901BEA68B411` (`ContactId` ASC) ,
CONSTRAINT `FK387E901BEA68B411`
FOREIGN KEY (`ContactId` )
REFERENCES `advcm_db`.`contact` (`id` ),
CONSTRAINT `fk_ContactField_contact`
FOREIGN KEY (`ContactId` )
REFERENCES `advcm_db`.`contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = 'It contains the value with a label';
-- -----------------------------------------------------
-- Table `advcm_db`.`contact_file`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `advcm_db`.`contact_file` (
`id` INT(11) NOT NULL AUTO_INCREMENT ,
`ContactId` INT(11) NOT NULL ,
`fileName` VARCHAR(255) NOT NULL ,
`fileType` VARCHAR(25) NOT NULL ,
`description` TINYTEXT NULL DEFAULT NULL ,
`createdOn` DATETIME NOT NULL ,
`lastUpdatedOn` DATETIME NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_ContactFile_contact` (`ContactId` ASC) ,
INDEX `FK4C251E3BEA68B411` (`ContactId` ASC) ,
CONSTRAINT `FK4C251E3BEA68B411`
FOREIGN KEY (`ContactId` )
REFERENCES `advcm_db`.`contact` (`id` ),
CONSTRAINT `fk_ContactFile_contact`
FOREIGN KEY (`ContactId` )
REFERENCES `advcm_db`.`contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `advcm_db`.`contact_group`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `advcm_db`.`contact_group` (
`id` INT(11) NOT NULL AUTO_INCREMENT ,
`ContactOwner` INT(11) NOT NULL ,
`name` VARCHAR(255) NOT NULL ,
`description` TINYTEXT NULL DEFAULT NULL ,
`color` VARCHAR(7) NULL DEFAULT NULL ,
`createdOn` DATETIME NOT NULL ,
`lastUpdatedOn` DATETIME NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_ContactGroup_contact` (`ContactOwner` ASC) ,
INDEX `FK3890E5A04FF86D69` (`ContactOwner` ASC) ,
CONSTRAINT `FK3890E5A04FF86D69`
FOREIGN KEY (`ContactOwner` )
REFERENCES `advcm_db`.`contact` (`id` ),
CONSTRAINT `fk_ContactGroup_contact`
FOREIGN KEY (`ContactOwner` )
REFERENCES `advcm_db`.`contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `advcm_db`.`contact_in_group`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `advcm_db`.`contact_in_group` (
`Contact` INT(11) NOT NULL ,
`ContactGroup` INT(11) NOT NULL ,
`insertedOn` DATETIME NOT NULL ,
PRIMARY KEY (`ContactGroup`, `Contact`) ,
INDEX `fk_ContactInGroup_Contact` (`Contact` ASC) ,
INDEX `fk_ContactInGroup_ContactGroup` (`ContactGroup` ASC) ,
INDEX `FK85EAE8243BA12EF6` (`Contact` ASC) ,
INDEX `FK85EAE824DB2B7A8` (`ContactGroup` ASC) ,
CONSTRAINT `contact_in_group_ibfk_1`
FOREIGN KEY (`ContactGroup` )
REFERENCES `advcm_db`.`contact_group` (`id` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `FK85EAE8243BA12EF6`
FOREIGN KEY (`Contact` )
REFERENCES `advcm_db`.`contact` (`id` ),
CONSTRAINT `FK85EAE824DB2B7A8`
FOREIGN KEY (`ContactGroup` )
REFERENCES `advcm_db`.`contact_group` (`id` ),
CONSTRAINT `fk_ContactInGroup_Contact`
FOREIGN KEY (`Contact` )
REFERENCES `advcm_db`.`contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
Seam-gen leerá la estructura de base de datos para generar las entidades JPA para nosotros.
Vamos a ver cómo utilizar esta característica al crear el proyecto.
Crear el proyecto
La creación del proyecto es muy similar a la que hemos usado para el ejemplo en el capítulo 3,
la única diferencia es que ahora se va a conectar nuestro proyecto para el DBMS MySQL. Por
lo tanto, tenemos que configurar de una manera adecuada.
Después de que el texto de bienvenida, vamos a entrar en nuestro nuevo proyecto de datos de
configuración (seam-gen guardará los datos anteriores, por lo que no tiene que volver a escribir
toda la información de configuración):
Muy bien, hemos hecho la configuración, y veremos cómo configurarlo para la conexión a un
DBMS MySQL en el siguiente capítulo (cuando vamos a empezar a hacer la aplicación real).
Por ahora las respuestas que está bien utilizar la opción predeterminada.
Estamos dispuestos a crear el proyecto utilizando los siguientes comandos para Microsoft
Windows y Unix / Linux / Mac OS X:
Ahora vamos a crear las entidades de la JPA (nuestro modelo de datos de la aplicación) de
forma automática mediante los siguientes comandos para Microsoft Windows y Unix/Linux/ Mac
OS X:
Si está utilizando el IDE de Eclipse, usted tiene que importar el proyecto en el área de trabajo
(que se describe cómo hacer que en el capítulo 2, Introducción). Con otros IDE (IntelliJ o
NetBeans), sólo se puede abrir desde la ubicación en la que he dicho seam-gen para crearlo.
El diagrama de clases
El diagrama de clases siguiente muestra las clases entidad JPA generados por seam-gen:
Como puede ver, no se diferencia mucho del diagrama ER-de hecho, cada clase se
corresponde con cada tabla en la base de datos.
Este es un ejemplo generado de la clase entidad (la entidad ContactGroup). Hemos escrito
algunas partes de la clase, sólo para mostrar algunos puntos relevantes de JPA:
@Entity
@Table(name = "contact_group",
catalog = "adv_contact_manager")
public class ContactGroup implements java.io.Serializable {
private Integer id;
private Contact contact;
private String name;
private Set<ContactInGroup> contactInGroups =
new HashSet<ContactInGroup>(0);
// ... other fields, constructors, getters and setters ...
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ContactOwner", nullable = false)
@NotNull
public Contact getContact() {
return this.contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
@Column(name = "name", nullable = false)
@NotNull
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY, mappedBy = "contactGroup")
public Set<ContactInGroup> getContactInGroups() {
return this.contactInGroups;
}
public void setContactInGroups(Set<ContactInGroup> contactInGroups)
{
this.contactInGroups = contactInGroups;
}
}
Es sólo una clase normal (con campos, accesores y modificadores) anotado por JPA
(destacamos éstas) que conectan la clase y sus campos con la tabla correspondiente en la
base de datos. Usted puede anotar los campos o el accesor (como en este caso). En ambos
casos, la anotación tiene los mismos efectos.
Otra anotación que debes conocer es JPA @OneToMany que administra la relación con otras
clases de entidad.
En este caso, estamos agregando mas validadores Hibernate a nuestras clases de entidad.
Éstos son algunos de utilidad incorporada en los:
Usted puede crear sus propios validadores de una manera sencilla. Consulte la documentación
Validadores de Hibernate para ver todas las características de este marco.
Otra característica útil que nos gustaría añadir a nuestras entidades es el representado por las
anotaciones @PrePersist y @PreUpdate. Si un método es anotado con una de estas
anotaciones, será invocado antes de la persistencia de la instancia en la base de datos y antes
de su actualización.
Aquí está el código añadido para la clase de entidad de la sección anterior (la entidad
ContactGroup):
/**
* This method initializes the values before
* the class is persisted
*/
@PrePersist
public void prePersist() {
setCreatedOn(new Date());
setLastUpdatedOn(new Date());
}
/**
* This method initializes the values before
* the class is updated
*/
@PreUpdate
public void preUpdate() {
setLastUpdatedOn(new Date());
}
Aquí usamos las anotaciones @PrePersist y @PreUpdate que nos permiten configurar
automáticamente las propiedades createdOn y lastUpdatedOn (utilizando el
setCreatedOn y métodos setUpdatedOn) sin tener que hacer que cada vez que persisten o
actualización de una entidad. También los utilizan para hacer algo antes de que la entidad se
conserva o actualizada.
También puede utilizar @PreRemove para anotar un método que será llamado después de que
se elimine la instancia correspondiente de la clase de entidad.
Para poder usarlo, también tenemos que agregar el espacio de nombres RichFaces que hemos
visto. Por ahora, así se debe de ver la página template.xhtml (algunas partes se han omitido
porque son los mismos que ya antes hemos visto):
<f:view contentType="text/html"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:s="http://jboss.com/products/seam/taglib">
<html>
<head>
...
</head>
<body>
<div class="body">
<rich:messages id="messages"
globalOnly="true"
styleClass="message"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"
rendered="#{showGlobalMessages!='false'}"/>
<ui:insert name="body"/>
</div>
...
</body>
</html>
</f:view>
Como puede ver, el atributo rendered del componente rich:messages está controlada por
la expresión EL #{showGlobalMessages!='false'}. showGlobalMessage es un
parámetro Facelet que se puede pasar a la plantilla (¿recuerda el parámetro projectName
pasados al menú, anteriormente?) para forzar no visualizar el componente rich:messages.
La página de menú
Esta es una página incluida (como hemos visto) en la plantilla y es un rich:toolBar con dos
instancias rich:toolBarGroup (uno en la izquierda y otro con alineación a la derecha):
<rich:toolBar
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:rich="http://richfaces.org/rich">
<rich:toolBarGroup>
<h:outputText value="#{projectName}:"/>
<s:link view="/home.xhtml" value="Home"
propagation="none"/>
</rich:toolBarGroup>
<rich:toolBarGroup location="right">
<h:outputText
value="signed in as: #{identity.username}"
rendered="#{identity.loggedIn}"/>
<s:link view="/login.xhtml" value="Login"
rendered="#{not identity.loggedIn}"
propagation="none"/>
<s:link view="/home.xhtml" action="#{identity.logout}"
value="Logout" rendered="#{identity.
loggedIn}"
propagation="none"/>
</rich:toolBarGroup>
</rich:toolBar>
Después de la declaración xmlns (utilizado por Facelets), podemos encontrar los dos grupos
con algunos textos y enlaces dentro. El grupo de la derecha contiene el enlace de entrada al
sistema si el usuario no está conectado, y la información del usuario y el enlace de
desconexión si el usuario se encuentra conectado. Por lo tanto, cuando ningún usuario está
conectado, la página es así:
Cuando un usuario está conectado, aparece como sigue:
Como puede ver, la parte en la parte derecha de la barra de menú cambia según el estado de
la variable identity.loggedIn.
Veamos el código:
Una vez más, vemos DOCTYPE y el componente Facelets ui:composition que obtiene los
contenidos que deben incluirse en otro Facelet. Como puede ver, de hecho, después de las
declaraciones xmlns, está la plantilla de atributo que apunta a la página template.xhtml
que hemos visto y lo utiliza como una plantilla.
La respuesta es simple: sólo tiene que utilizar la etiqueta <ui:define> y establecer el atributo
de nombre como uno de los puntos de inserción:
<ui:define name="body">
<!—- this code will be inserted -->
</ui:define>
De todos los componentes hay que apuntar a un componente Seam especial llamada
identidad, que administra la fase de inicio de sesión.
No vamos a editar mucho esta página, lo único que se hará es eliminar la sección Note, ya que
no se necesitará:
<p>
<i>Note - </i> You may login with the username 'admin' and a blank
password.
</p>
La página de inicio
La página de inicio (home.xhtml) es una página en blanco con un cuadro que muestra
Powered by seam y Generated by seam-gen.
Por ahora, simplemente borre el código de la caja a fin de obtener una página en blanco, que
se llenará en los capítulos siguientes. El código es el siguiente:
Y esta es la forma en que aparece (se puede ver la barra de menú que viene de la plantilla):
Resumen
En este capítulo, hemos creado las bases de nuestro proyecto para tener una idea de las
tecnologías de nuestro lado para que sepamos, como construir buenas aplicaciones, mientras
que empecemos a ser más productivos y rápidos.
Hemos visto el proyecto seam proyecto generado por seam-gen e incluye soporte para Ajax y
los componentes ricos (con RichFaces). Además, vimos las plantillas (con Facelets) y la
autenticación de JBoss Seam. También hemos empezado a personalizar un poco las entidades
(agregando anotaciones Hibernate Validator) y el código XHTML.