Primeros pasos con JavaServer Faces usando Eclipse Este tutorial ayuda a dar los primeros pasos con el absolutamente nuevo framework JavaServer Faces (JSF). Una aplicación de ejemplo (una biblioteca) será creada paso a paso ilustrando los diferentes elementos del framework. La aplicación de ejemplo tendrá las siguientes funcionalidades: • Mostrar una descripción del libro (listado de libros) • Agregar, editar y borrar un libro. Generales Autor: Sascha Wolski http://www.laliluna.de/tutorials.html Tutorials for Struts, EJB, xdoclet, JSF, JSP and eclipse. Traducción: Sebastián Arechederreta Nota de la traducción: Algunos terminos en inglés se mantienen para evitar confusiones con otras palabras. Las opciones de Eclipse no han sido traducidas ni las capturas de pantallas ni el código fuente a fin de poder seguir otros materiales publicados en el sitio de laliluna. Fecha: 21 de Diciembre de 2004 Código fuente: Los fuentes no incluyen archivos de proyecto de eclipse o bibliotecas. Cree un nuevo proyecto siguiendo el tutorial, agregue las bibliotecas tal cual se explica en el tutorial y luego copie el código fuente en su nuevo proyecto. http://www.laliluna.de/assets/tutorials/first-java-server-faces-tutorial.zip Version PDF del Tutorial (inglés): http://www.laliluna.de/assets/tutorials/first-java-server-faces-tutorial-es.pdf Herramientas para el desarrollo. Eclipse 3.x MyEclipse plugin 3.8 (Una poderosa y barata extensión para Eclipse para desarrollo de Aplicaciones Web y EJB (J2EE). Creo que hay una versión de prueba disponible en MyEclipse.) Servidor de Aplicación Jboss 3.2.5 Aquí se puede usar Tomcat si se quiere. Creación de un proyecto JavaServer faces Cree un nuev proyecto web. File > New > Project. Ponga un buen nombre y agregue las bibliotecas JSTL al proyecto. Agregue las capacidadades JavaServer faces. Botón derecho sobre el proyecto y seleccione MyEclipse > Add JSF Capabilities. La clase Book (Libro) Agregue un nuevo paquete de.laliluna.tutorial.library y cree una nueva clase Book. Abra la clase y agregue las siguientes propiedades privadas: • id • author • title • available Genere los métodos get y set para cada propiedad. Botón derecho sobre laventana de edición y elija Source > Generate Getter- and Setter Methods. Además tiene que agregar un constructor, para que establezca las propiedades si inicializa una instancia del nuevo objeto. El siguiente código muestra la clase book. public class Book implements Serializable { // private private private private ------------------ Propiedades long id; String author; String title; boolean available; -------------------------------- // ------------------ Constructores ------------------------------public Book(){} public Book(long id, String author, String title, boolean available){ this.id = id; this.author = author; this.title = title; this.available = available; } // ------------------ Métodos Get public String getAuthor() { y set --------------------- } return author; } public void setAuthor(String author) { this.author = author; } public boolean isAvailable() { return available; } public void setAvailable(boolean available) { this.available = available; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } Agregue un get y set para la clase. /** * Establece las propiedades * @param book */ public void setBook(Book book){ this.setId(book.getId()); this.setAuthor(book.getAuthor()); this.setTitle(book.getTitle()); this.setAvailable(book.isAvailable()); } /** * @return objeto book */ public Book getBook(){ } return new Book(this.getId(), this.getAuthor(), this.getTitle(), this.isAvailable()); La clase database Usamos una clase para obtener algunos datos de prueba sin necesidad de usar una base de datos. Descargue la aplicación de ejemplo de este tutorial y copie la clase SimulateDB.java que está en la carpeta src/de/laliluna/tutorial/library/ en el paquete de.laliluna.tutorial.library. La clase BookList Cree una class BookList en el paquete de.laliluna.library. Esta clase incluye las propiedades de libros, que se presentan en la lista de libros. Genere los métodos get y set para las propiedades de los libros y cambie el método get como sigue. public class BookList { // ------------------------- Propiedades --------------------------Collection books; // ------------------------- Get y Set -------------------/** * @return collection de libros */ public Collection getBooks(){ SimulateDB simulateDB = new SimulateDB(); /* Obtiene el session map del contexto externo */ Map session = FacesContext.getCurrentInstance(). getExternalContext().getSessionMap(); /* Lies alle Bücher auf der simulierten Datenbank aus */ books = simulateDB.getAllBooks(session); } } return books; /** * @param books Los libros a definir. */ public void setBooks(Collection books) { this.books = books; } Su explorador de paquete se parecerá a la siguiente imagen. Métodos Action listener (interface de oyente) Para permitir que el usuario pueda agergar, editar o boorar un libro, tenemos que incluir las funcionalidades apropiadas. Estas funcionalidades serán implementadas en métodos / clases action listener. Si un evento ocurre (por ej.: un usuario hace clic en un enlace) un método action listener será llamado y procesado. Abra la clase Book y agregue cuatro métodos, que efectuen las siguientes funcionalidades. • Inicializar un libro • Editar un libro • Guardar un libro • Borrar un libro Inicializar un libro /** * Inicia las propiedades de la clase con * @param event */ public void initBook(ActionEvent event){ } null /* * inicia el objeto book */ this.setBook(new Book()); Editar un libro /** * Obtiene el libro a editar y lo asigna al bean * * @param event */ public void selectBook(ActionEvent event){ SimulateDB simulateDB = new SimulateDB(); /* * Obtiene el session map del contexto externo */ Map session = FacesContext.getCurrentInstance().getExternalContext(). getSessionMap(); /* * Encuentra el componente UIParameter component por expresión */ UIParameter component = (UIParameter) event.getComponent().findComponent ("editId"); } /* * ajusta el valor del componente UIParameter */ long id = Long.parseLong(component.getValue().toString()); /* * obtiene el libro por id y lo pone en una propiedad loca */ this.setBook(simulateDB.loadBookById(id, session)); Guardar un libro /** * Agrega o actualiza un libro en la base de datos simulada. * Si el id del libro no existe el libro se agregará * de lo contrario será actualizado * * @param event */ public void saveBook(ActionEvent event){ SimulateDB simulateDB = new SimulateDB(); /* * Obtiene el session map del contexto externo */ Map session = FacesContext.getCurrentInstance().getExternalContext(). getSessionMap(); } /* * Agrega o actualiza el libro en la base de datos simulada. */ simulateDB.saveToDB(this.getBook(), session); Borrar un libro /** * Elimina un libro de la base de datos simulada * * @param event */ public void deleteBook(ActionEvent event){ SimulateDB simulateDB = new SimulateDB(); /* * Obtiene el session map del contexto externo */ Map session = FacesContext.getCurrentInstance().getExternalContext(). getSessionMap(); /* * Encuentra el componente UIParameter por expresión */ UIParameter component = (UIParameter) event.getComponent().findComponent ("deleteId"); /* * ajusta el valo del componente UIParameter */ long id = Long.parseLong(component.getValue().toString()); } /* * Elimina el libro por id */ simulateDB.deleteBookById(id, session); El archivo faces-config.xml El archivo faces-config.xml es el archivo de configuración central de JavaServer faces. Aquí se define el flujo de trabajo de la applicación (en que acción que sitio será procesado) , el manejo de las clases bean por JSF y otras cosas más. El flujo de trabajo de la aplicación biblioteca es como se muestra a continuación: Definimos una regla de navegación para esta flujo de trabajo. Abra el archivo faces-config.xml y agregue la siguiente configuración. <faces-config> <!-- Reglas de navegación --> <navigation-rule> <description>Lista de libros</description> <from-view-id>/listBooks.jsp</from-view-id> <navigation-case> <from-outcome>editBook</from-outcome> <to-view-id>/editBook.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <description>Agrega o edita un libro</description> <from-view-id>/editBook.jsp</from-view-id> <navigation-case> <from-outcome>listBooks</from-outcome> <to-view-id>/listBooks.jsp</to-view-id> <redirect/> </navigation-case> </navigation-rule> </faces-config> <navigation-rule> Define una regla de navegación <from-view-id>/listBooks.jsp</from-view-id> Define el archivo jsp para el que la regla de navegación es relevante. <navigation-case> Define un caso de navegación <from-outcome>editBook</from-outcome> Define el nombre para este caso de navegación <to-view-id>/listBooks.jsp</to-view-id> Redirecciona al archivo JSP <redirect/> Todos los parámetros guardados en la solicitud se perderán cuando establezca esta marca. Si quiere acceder a las clases bean en sus archivos JSP, tiene que registrar las clases bean en facesconfig.xml Agregue el siguiente código. <!-- Gestión de beans --> <managed-bean> <description> Book bean </description> <managed-bean-name>bookBean</managed-bean-name> <managed-bean-class>de.laliluna.tutorial.library.Book</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> <managed-bean> <description> BookList Bean </description> <managed-bean-name>bookListBean</managed-bean-name> <managed-bean-class>de.laliluna.tutorial.library.BookList</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> Define un bean gestionado <managed-bean-name>bookBean</managed-bean-name> Define un nombre para el bean gestionado. Este nombre es el que se usa en el archivo JSP. <managed-bean-class>de.laliluna.tutorial.library.Book</managed-bean-class> Define la clase que representa el bean. <managed-bean-scope>request</managed-bean-scope> Define en que estructura el bean es guardado. Creación de archivos JSP Como primer paso creamos un archivo JSP que se llame index.jsp, el cual llevará al usuario al listado de libros. index.jsp <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> <jsp:forward page="/listBooks.faces" /> </body> </html> El segundo paso será crear el listado de libros. listBooks.jsp <%@ page language="java" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName() +":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>Listado de libros</title> </head> <body> <f:view> <h:form id="bookList"> <h:dataTable id="books" value="#{bookListBean.books}" var="book" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Autor"/> </f:facet> <h:outputText value="#{book.author}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Titulo"/> </f:facet> <h:outputText value="#{book.title}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Disponible"/> </f:facet> <h:selectBooleanCheckbox disabled="true" value="#{book.available}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Editar"/> </f:facet> <h:commandLink id="Edit" action="editBook" actionListener="#{bookBean.selectBook}"> <h:outputText value="Editar" /> <f:param id="editId" name="id" value="#{book.id}" /> </h:commandLink> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Eliminar"/> </f:facet> <h:commandLink id="Delete" action="listBooks" actionListener="#{bookBean.deleteBook}"> <h:outputText value="Eliminar" /> <f:param id="deleteId" name="id" value="#{book.id}" /> </h:commandLink> </h:column> </h:dataTable> <h:commandLink id="Add" action="editBook" actionListener="#{bookBean.initBook}"> <h:outputText value="Agregar un libro" /> </h:commandLink> </h:form> </f:view> </body> </html> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> Con la directiva taglib incluimos la biblioteca de marcas JSF <f:view> Confecciona un componente de vista. Todas las otras marcas deben estar incuidas dentro de este tag. <h:form id="bookList"> Define un formulario HTML. <h:dataTable id="books" value="#{bookListBean.books}" var="book" border="1"> Define una tabla HTML. La marca es usada para recorrer la lista de datos como un bucle for. El valor del parámetro asigna una lista de datos, en nuestro caso la lista de libros de la biblioteca. Con el parámetro var se define la variable usada para acceder a un elemento (un libro) de la lista con la marca (loop). <h:column> <f:facet name="header"> <h:outputText value="Autor"/> </f:facet> <h:outputText value="#{book.author}" /> </h:column> Muestra una columna con su cabecera. <f:facet name="header"> muestra la cabecera <h:outputText value="Autor"/> imprime una etiqueta en la cabecera. <h:outputText value="#{book.author}" /> referencia a la propiedad author del elemento actual de la lista. <h:commandLink id="Edit" action="editBook" actionListener="#{bookBean.selectBook}"> Muestra un enlace HTML, que envia el formulario. El parámetro action define el caso de navegación, que tiene que ser procesado al enviar el formulario. En nuestro caso es el caso editBook, que hemos agregador antes en faces-config.xml. Asignamos el método action listener al enlace con el parámetro actionListener. Después que el usuario envie el formulario el método será procesado. El último archivo JSP incluye un formulario para agregar o editar un libro. editBook.jsp <%@ page language="java" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName() +":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>Agrega / Edita un libro</title> </head> <body> <f:view> <h:form> <h:inputHidden id="id" value="#{bookBean.id}"/> <h:panelGrid columns="2" border="1"> <h:outputText value="Autor:" /> <h:inputText id="author" value="#{bookBean.author}"> </h:inputText> <h:outputText value="Titulo:" /> <h:inputText id="title" value="#{bookBean.title}"> </h:inputText> <h:outputText value="Disponible:" /> <h:selectBooleanCheckbox id="available" value="#{bookBean.available}" /> </h:panelGrid> <h:commandButton value="Guardar" action="listBooks" actionListener="#{bookBean.saveBook}" /> </h:form> </f:view> </body> </html> <h:inputHidden id="id" value="#{bookBean.id}"/> Define un elemento oculto HTML. Value se refiere al bean bookBean que sera gestionado y su propiedad id que está indicada en el archivo faces-config.xml. <h:panelGrid columns="2" border="1"> Muestra una tabla HTML con 2 columnas. <h:inputText id="author" value="#{bookBean.author}"> Define un campo de texto HTML. Value se refiere a la propiedad author de nuestra clase Book. <h:commandButton value="Guardar" action="listBooks" actionListener="#{bookBean.saveBook}" /> Define un botón de envío HTML con el valor Guardar y la acción listBooks. El método action listener saveBook será procesado si el usuario envía el formulario. Prueba de la aplicación Inicie jboss y genere el proyecto como un Packaged Archive. Llame al proyecto ahora http://localhost:8080/LibraryWeb/