Desarrollo con Java Publicación Armando Arce 09 de June de 2016 Índice general 1. Contenidos 3 I II Desarrollo con Java, Publicación El objetivo de este sitio es presentar un conjunto de tutoriales básicos sobre el desarrollo de aplicaciones empresariales utilizando el ambiente Java. Los tutoriales disponibles hasta el momento son los siguientes: Índice general 1 Desarrollo con Java, Publicación 2 Índice general CAPÍTULO 1 Contenidos 1.1 Uso de JSP Una de las tecnologías más ampliamente utilizadas para el desarrollo de aplicaciones Web bajo ambiente Java es JSP (Java Server Pages). Esta herramienta permite crear una serie de plantillas HTML que incluyen código incrustado (llamados sniplets) mediante marcadores especiales. El uso de JSP simplifica la programación de servlets pues no es necesario compilar código ya que esto se realiza de forma automática. Además, debido a que la plantilla se escribe directamente en HTML es factible utilizar editores y diseñadores de páginas Web para programar la aplicación. 1.1.1 Creación de la base de datos Para este ejemplo se creará una base de datos de prueba, llamada universidad.db, utilizando SQLite. Es posible utilizar diferentes herramientas para crear bases de datos en este formato, entre ellas se encuentra el plugin para Firefox llamado SQLite Manager, o bien, la herramienta SQLiteBrowser. 3 Desarrollo con Java, Publicación Por el momento solo se utilizará una tabla con información de profesores de una universidad. El código SQL para crear dicha tabla sería el siguiente: CREATE TABLE "profesor" ( id INTEGER PRIMARY KEY ASC, cedula VARCHAR, nombre VARCHAR, titulo VARCHAR, area VARCHAR, telefono VARCHAR ); Luego de crear la base de datos se agregaron algunos datos de ejemplo a esta tabla. Las siguientes instrucciones SQL permiten poblar la tabla alguna información: INSERT INTO profesor (id,cedula,nombre,titulo,area,telefono) VALUES (1,'101110111','Carlos Perez Rojas','Licenciado', 'Administracion','3456-7890'); INSERT INTO profesor (id,cedula,nombre,titulo,area,telefono) VALUES (2,'202220222','Luis Torres','Master', 'Economia','6677-3456'); INSERT INTO profesor (id,cedula,nombre,titulo,area,telefono) VALUES (3,'303330333','Juan Castro','Licenciado', 'Matematica','67455-7788'); 1.1.2 Página de listado de profesores La primer página consistirá del listado de todos los profesores que se encuentran en la base de datos. El código que se muestra a continuación (llamado listaProfesores.jsp) establece la conexión con la base de datos (utilizado JDBC), ejecuta la instrucción de consulta, recupera los datos y crea una tabla HTML con la información obtenida. <%@ page import="java.sql.*" %> <% String driver="org.sqlite.JDBC"; String url="jdbc:sqlite:database/universidad.db"; 4 Capítulo 1. Contenidos Desarrollo con Java, Publicación Class.forName(driver); Connection conn=null; try { conn = DriverManager.getConnection(url); String sql="select * from profesor"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Listado de profesores</h2> <table> <thead> <tr><th>Cedula</th><th>Nombre</th> <th>Titulo</th><th>Acciones</th></tr> </thead> <tbody> <% while (rs.next()) { String id = rs.getString(1); String cedula = rs.getString(2); String nombre = rs.getString(3); String titulo = rs.getString(4); %> <tr><td><%= cedula %></td> <td><%= nombre %></td> <td><%= titulo %></td> <td><a href='/detalleProfesor.jsp?id=<%= id %>'> <input type="submit" value="Detalle"/></a> <a href='/eliminarProfesor.jsp?id=<%= id %>'> <input type="submit" value="Eliminar"/></a></td></t <% } %> </tbody> <tfoot> <tr><td><a href='/agregarProfesor.jsp'> <input type="submit" name="action" value="Agregar"/></a> </td><td></td><td></td><td></td></tr> 1.1. Uso de JSP 5 Desarrollo con Java, Publicación </tfoot> </table> </html> <% rs.close();rs=null; stmt.close();stmt=null; if (conn!=null) conn.close(); } catch (Exception e) { e.printStackTrace(); } %> Es importante observar en este código que existen enlaces que accederán a otras páginas para realizar acciones con los datos. Por ejemplo, el enlace de “Detalle” ejecutará la página detalleProfesor.jsp con el parámetro ID; y el enlace de “Eliminar” ejecutará la página eliminarProfesor.jsp con el mismo parámetro ID. También está presente otro enlace “Agregar” que invocará a la página agregarProfesor.jsp pero sin parámetros. 1.1.3 Detalle del profesor La página detalleProfesor.jsp recibe como parámetro el ID de un profesor, recupera sus datos desde la base y datos, y los muestra en un formulario HTML. La estructura general de la consulta a la base de datos es muy similar a la anterior con la diferencia que se recupera solo un registro particular. <%@ page import="java.sql.*" %> <% String id = request.getParameter("id"); String driver="org.sqlite.JDBC"; String url="jdbc:sqlite:database/universidad.db"; Class.forName(driver); Connection conn=null; try { conn = DriverManager.getConnection(url); 6 Capítulo 1. Contenidos Desarrollo con Java, Publicación String sql="select * from profesor where id='"+id+"'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Detalle de Profesor</h2> <form name="ActualizarProfesor" action="actualizarProfesor" met <table style="width:400px;"> <thead> <tr><th></th><th></th></tr> </thead> <tbody> <% rs.next(); String cedula = rs.getString(2); String nombre = rs.getString(3); String titulo = rs.getString(4); String area = rs.getString(5); String telefono = rs.getString(6); %> <input type="hidden" name="id" value="<%= id %>"/> <tr><td>Nombre:</td><td> <input type="text" name="nombre" value="<%= nombre %>"/></t <tr><td>Cedula:</td><td> <input type="text" name="cedula" value="<%= cedula %>"/></t <tr><td>Titulo:</td><td> <input type="text" name="titulo" value="<%= titulo %>"/></t <tr><td>Area:</td><td> <input type="text" name="area" value="<%= area %>"/></td></ <tr><td>Telefono:</td><td> <input type="text" name="telefono" value="<%= telefono %>"/ </tbody> <tfoot> <tr><td><input type="submit" value="Actualizar" /></td><td></ </tfoot> </tbody> 1.1. Uso de JSP 7 Desarrollo con Java, Publicación </table> </form> </html> <% rs.close();rs=null; stmt.close();stmt=null; if (conn!=null) conn.close(); } catch (Exception e) { e.printStackTrace(); } %> Este código también cuenta con un enlace adicional “Actualizar” que permite tomar la información del formulario y realizar la actualización de datos en la base de datos, mediante la página actualizarProfesor.jsp 1.1.4 Hoja de estilo Con el fin de mejorar la apariencia de este ejemplo se ha desarrollado una pequeña hoja de estilo (css) que permite organizar mejor los datos de la tabla y el formulario. El archivo llamado style.css cuenta con el siguiente código: body { line-height: 1.6em; font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; color: #009; } table { font-size: 12px; margin: 45px; width: 480px; text-align: left; border-collapse: collapse; } th, tfoot td { font-size: 13px; 8 Capítulo 1. Contenidos Desarrollo con Java, Publicación font-weight: normal; padding: 8px; background: #b9c9fe; border-top: 4px solid #aabcfe; border-bottom: 1px solid #fff; color: #039; text-align: center; } td { padding: 8px; background: #e8edff; border-bottom: 1px solid #fff; color: #669; border-top: 1px solid transparent; } tr:hover td { background: #d0dafd; color: #339; } input[type="text"] { width: 250px; } 1.1.5 Ambiente de ejecución Para ejecutar este ejemplo es necesario contar con un servidor de servlets que permita también la ejecución de plantillas JSP. La herramienta más utilizada para esto es el Apache Tomcat, el cuál es muy potente y cuenta con gran cantidad de parámetros de configuración. Sin embargo, para propósito de desarrollo y depuración de programas basta con un ambiente más liviano tal como Winstone. Winstone consiste de un único archivo de menos de 350 KB, llamado winstone-0.9.10.jar, el cual puede ser ejecutado directamente mediante Java. Sin embargo, poder utilizar plantillas JSP se requiere de la herramienta Jasper que consiste de múltiples librerías adicionales. Para acceder a la base de datos SQLite, mediante JDBC, es necesario contar con una librería que incluya el driver adecuado. Aún cuando 1.1. Uso de JSP 9 Desarrollo con Java, Publicación existen diferentes librerías que hacen esto, ninguna es pequeña. Estructura de directorios La ubicación de los diferentes archivos de código, y librerías se muestra en el siguiente esquema de directorios: tutorial1 run.bat winstone-0.9.10.jar database universidad.db root style.css listaProfesores.jsp detalleProfesor.jsp lib el-api-6.0.18.jar jasper-6.0.18.jar jasper-el-6.0.18.jar jsp-api-6.0.18.jar juli-6.0.18.jar servlet-api-2.5.jar sqlite-jdbc-3.5.9.jar 1.1.6 Ejecución del ejemplo Teniendo instalado el JDK de Java (no el JRE) basta con ejecutar el archivo run.bat para iniciar el servidor. El archivo run.bat cuenta con las siguientes instrucciones: set PATH=C:\Java\jdk1.7.0\bin;%PATH% java -jar winstone-0.9.10.jar --httpPort=8089 ^ --commonLibFolder=lib --useJasper=true --webroot=root Luego se debe apuntar el visualizador (browser) de Web a la dirección http://localhost:8089/listaProfesores.jsp 10 Capítulo 1. Contenidos Desarrollo con Java, Publicación Nota: Es posible que el JDK de Java se encuentre instalado en otro directorio en su máquina. 1.2 Rutinas de Transacción A continuación se presenta un ejemplo sencillo de la aplicación Web del Sistema Universitario, lo que servirá para mostrar el uso de diferentes capas y de cómo se debe estructurar una aplicación para obtener el mayor provecho de las aplicaciones empresariales. En este ejemplo se combinarán tres patrones descritos por Fowler: rutinas de transacción para la lógica del dominio, pasarela a fila de datos para la capa de acceso a los datos, y controlador frontal para la capa de presentación. Más específicamente, las rutinas de transacción se estructurarán utilizando el patrón comando. 1.2.1 Capa de acceso a datos La capa de acceso a datos utilizará una pasarela a filas de datos que se conectará a una base de datos SQLite. Este tipo de técnica también requiere crear una clase que se encargue de crear la pasarela, llamada un “finder”. Base de datos Se utilizará una base de datos SQLite para administrar los datos. Dicha base de datos debe llevar por nombre universidad.db y debe estar ubicada en el directorio /tutorial2/database/. El código SQL utilizado para generar la tabla de profesores sería el siguiente: CREATE TABLE profesor (id INTEGER PRIMARY KEY, cedula VARCHAR, nombre VARCHAR, titulo VARCHAR, area VARCHAR, telefono VARCHAR) INSERT INTO profesor VALUES(1,'101110111','Carlos Perez','Licenci 'Administracion','3456-7890'); INSERT INTO profesor VALUES(2,'202220222','Luis Torres','Master', 1.2. Rutinas de Transacción 11 Desarrollo con Java, Publicación 'Economia','6677-3456'); INSERT INTO profesor VALUES(3,'303330333','Juan Castro','Licencia 'Matematica','6755-7788'); Para administrar una base de datos SQLite se puede utilizar alguno de los excelentes productos creados para ello, tal como SQLiteman ó el plugin para Firefox llamado SQLite Manager Pasarela a fila de datos Para implementar la capa de acceso de datos se utiliza una pasarela a filas de datos. Para ello es necesario crear una clase que permita acceder a la información de la base de datos cuyo nombre es ProfesorRowGateway.java. Dicha clase residirá en el directorio /tutorial2/src/data/ package data; import import import import java.util.*; java.sql.*; javax.sql.*; org.springframework.jdbc.core.JdbcTemplate; public class ProfesorRowGateway { private private private private private private private private int id; String cedula; String nombre; String titulo; String area; String telefono; JdbcTemplate jdbcTemplate; DataSource dataSource; ProfesorRowGateway() {}; ProfesorRowGateway(int id, String ced, String nomb, String tit, String area, String t this.id=id; this.cedula=ced; this.nombre=nomb; 12 Capítulo 1. Contenidos Desarrollo con Java, Publicación this.titulo=tit;this.area=area;this.telefono=tel; } public void setId(int id) {this.id = id;} public int getId() {return id;} public void setCedula(String ced) {this.cedula=ced;} public String getCedula() {return cedula;} public void setNombre(String nomb) {this.nombre=nomb;} public String getNombre() {return nombre;} public void setTitulo(String tit) {this.titulo=tit;} public String getTitulo() {return titulo;} public void setArea(String area) {this.area=area;} public String getArea() {return area;} public void setTelefono(String tel) {this.telefono=tel;} public String getTelefono() {return telefono;} public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } private void createJdbcTemplate() { jdbcTemplate = new JdbcTemplate(dataSource); } private static final String insertStatement = "INSERT INTO profesor "+ "VALUES (?,?,?,?,?,?)"; public int insert() { Random generator = new Random(); int id = generator.nextInt(); if (jdbcTemplate==null) createJdbcTemplate(); jdbcTemplate.update(insertStatement, id,cedula,nombre,titulo,area,telefono); return id; 1.2. Rutinas de Transacción 13 Desarrollo con Java, Publicación } private static final String updateStatement = "UPDATE profesor "+ "SET cedula = ?, nombre = ?, titulo = ?, "+ "area = ?, telefono = ? WHERE id = ?"; public void update() { if (jdbcTemplate==null) createJdbcTemplate(); jdbcTemplate.update(updateStatement, cedula,nombre,titulo,area,telefono,id); } private static final String deleteStatement = "DELETE FROM profesor "+ "WHERE id = ?"; public void delete() { if (jdbcTemplate==null) createJdbcTemplate(); jdbcTemplate.update(deleteStatement,id); } public static ProfesorRowGateway load(DataSource ds, Map map) { ProfesorRowGateway prof=null; if (map==null) prof = new ProfesorRowGateway(); else { int id = ((Integer)map.get("id")).intValue(); String cedula = (String)map.get("cedula"); String nombre = (String)map.get("nombre"); String titulo = (String)map.get("titulo"); String area = (String)map.get("area"); String telefono = (String)map.get("telefono"); prof = new ProfesorRowGateway(id,cedula,nombre,titulo,a } prof.setDataSource(ds); return prof; } } 14 Capítulo 1. Contenidos Desarrollo con Java, Publicación Generación de objetos de pasarelas Para manejar este tipo de técnica es necesario contar con otra clase encargada de crear las pasarelas. Esta nueva clase se define en el archivo ProfesorFinder.java y reside en el mismo directorio /tutorial2/src/data/ package data; import import import import java.util.*; java.sql.*; javax.sql.*; org.springframework.jdbc.core.JdbcTemplate; public class ProfesorFinder { private JdbcTemplate jdbcTemplate; private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; this.jdbcTemplate = new JdbcTemplate(dataSource); } public ProfesorRowGateway create() { return ProfesorRowGateway.load(dataSource,null); } private final static String findStatement = "SELECT * "+ "FROM profesor "+ "WHERE id = ?"; public ProfesorRowGateway find(String id) { List profs = jdbcTemplate.queryForList(findStatement,id); ProfesorRowGateway prof = ProfesorRowGateway.load(dataSource,(Map)profs.get(0)); return prof; } 1.2. Rutinas de Transacción 15 Desarrollo con Java, Publicación private final static String findAllStatement = "SELECT * "+ "FROM profesor "; public List<ProfesorRowGateway> findAll() { List result = new ArrayList(); List profs = jdbcTemplate.queryForList(findAllStatement); for (int i=0; i<profs.size();i++) result.add(ProfesorRowGateway.load(dataSource,(Map)prof return result; } } Compilando la capa de datos Se puede realizar la compilación de estas dos clases en forma separada del resto del código. Para ello es necesario contar con el framework Spring 3 el cual se debe descargar y copiar todas las librerías .jar del directorio lib de dicho framework hacia el directorio /tutorial2/root/WEB-INF/lib/. También es necesario contar en dicho directorio con el driver jdbc para SQLite y las librerías commons para manejo de conexiones a base de datos. Específicamente las librerías que deben residir en el directorio /tutorial2/root/WEB-INF/lib/ son las siguientes: commons-dbcp-1.4.jar commons-logging-1.1.1.jar commons-pool-1.6.jar servlet-api.jar spring-asm-3.2.0.M1.jar spring-beans-3.2.0.M1.jar spring-context-3.2.0.M1.jar spring-core-3.2.0.M1.jar 16 Capítulo 1. Contenidos Desarrollo con Java, Publicación spring-expression-3.2.0.M1.jar spring-jdbc-3.2.0.M1.jar spring-tx.3.2.0.M1.jar spring-web-3.2.0.M1.jar sqlite-jdbc-3.5.9.jar La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDataLayer.bat residente en el directorio /tutorial2 (todo en una sola línea): javac -cp "root/WEB-INF/lib/*" -d root/WEB-INF/classes src/data/ProfesorRowGateway.java src/data/ProfesorFinder.java Nota: La versión del JDK debe ser superior a 6.0 1.2.2 Capa de presentación El servicio de la universidad ha sido implementado como un controlador frontal, en donde cada rutina de transacción será implementada como un comando. El archivo del servicio se llamará FrontController.java y debe residir en el directorio /tutorial2/src/display/ package display; import import import import java.io.*; java.util.*; javax.servlet.*; javax.servlet.http.*; import org.springframework.web.context.*; import org.springframework.web.context.support.*; public class FrontController extends HttpServlet { private WebApplicationContext context; public void init(ServletConfig config) throws ServletException 1.2. Rutinas de Transacción 17 Desarrollo con Java, Publicación super.init(config); context = WebApplicationContextUtils.getWebApplicationContext(get } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException { FrontCommand command = getCommand((String)request.getAttribute("command")); command.init(context,request,response); command.process(); } private FrontCommand getCommand(String commandName) { try { return (FrontCommand) getCommandClass(commandName).newI } catch (Exception e) { e.printStackTrace(); } return null; } private Class getCommandClass(String commandName) { Class result; try { result = Class.forName(commandName); } catch (ClassNotFoundException e) { result = UnknownCommand.class; } return result; } } La clase para los comandos Como se indicó antes, cada transacción se implementa como un comando independiente de todos los demás. Existe una clase abstracta FrontCommand.java que permite definir los métodos comunes a cual18 Capítulo 1. Contenidos Desarrollo con Java, Publicación quier comando. Este archivo también se ubica en el directorio /tutorial2/src/display/ package display; import java.util.*; import import import import java.io.*; javax.servlet.*; javax.servlet.http.*; org.springframework.web.context.*; public abstract class FrontCommand { public WebApplicationContext context; public HttpServletRequest request; public HttpServletResponse response; public void init(WebApplicationContext ctx, HttpServletRequest req, HttpServletResponse resp) { this.context = ctx; this.request = req; this.response = resp; } protected void forward(String target) throws ServletException, IOException { RequestDispatcher dispatcher = context.getServletContext().getRequestDispatcher(target dispatcher.forward(request,response); } public abstract void process() throws ServletException, IOException; } 1.2. Rutinas de Transacción 19 Desarrollo con Java, Publicación Comandos desconocidos Cuando se pretende ejecutar un comando desconocido es necesario interceptar dicha solicitud e imprimir un mensaje de advertencia. La clase UnknownCommand.java se encarga de esta tarea y reside en el mismo directorio /tutorial2/src/display/ package display; import java.io.*; import javax.servlet.*; public class UnknownCommand extends FrontCommand { public void process() throws ServletException, IOException { forward("/unknown.jsp"); } } También es necesario contar con una pequeña plantilla JSP que permite generar el mensaje que se presentará en pantalla. El código de esta plantilla se ubica en el archivo unknown.jsp que reside en el directorio /tutorial2/root/ <html> <head> <title>Sistema Universitario</title> </head> <h1>Operación inválida</h1> </html> Compilando la capa de presentación También, se puede realizar la compilación de estas clases de presentación en forma separada del resto del código de la aplicación. Para ello es necesario contar con las librerías del framework Spring 3 (como se indicó antes) y con la librería servlet-api.jar ubicadas en el directorio /tutorial2/root/WEB-INF/lib/. La siguiente instrucción para ejecutar 20 Capítulo 1. Contenidos Desarrollo con Java, Publicación la compilación puede estar definida en un archivo compileDisplayLayer.bat residente en el directorio /tutorial2/ (todo en una sola línea): javac -cp "root/WEB-INF/lib/*" -d root/WEB-INF/classes src/display/FrontController.java src/display/FrontCommand.jav src/display/UnknownCommand.java 1.2.3 La capa de lógica del dominio Para implementar la capa de lógica del dominio se utilizará la técnica de rutinas de transacción. En dicho caso cada rutina es responsable de recuperar los parámetros de la consulta, acceder a la tabla de datos, procesar los datos, e invocar a la rutina de presentación. La rutina de listado Para presentar el listado de profesores se crea la clase ListaProfesores.java en el directorio /tutorial2/src/domain/ package domain; import display.FrontCommand; import data.ProfesorRowGateway; import data.ProfesorFinder; import import import import import import import java.util.Map; java.util.HashMap; java.util.List; java.util.ArrayList; java.io.IOException; java.sql.SQLException; javax.servlet.ServletException; public class ListaProfesores extends FrontCommand { public void process() throws ServletException, IOException { ProfesorFinder profs = 1.2. Rutinas de Transacción 21 Desarrollo con Java, Publicación (ProfesorFinder) context.getBean("profesorFinder"); List<ProfesorRowGateway> data = profs.findAll(); List param = new ArrayList(); for (int i=0;i<data.size();i++) { ProfesorRowGateway prof = data.get(i); Map item = new HashMap(); item.put("id",prof.getId()+""); item.put("cedula",prof.getCedula()); item.put("nombre",prof.getNombre()); item.put("titulo",prof.getTitulo()); item.put("area",prof.getArea()); item.put("telefono",prof.getTelefono()); param.add(item); } request.setAttribute("profesores",param); forward("/listaProfesores.jsp"); } } Plantilla JSP Adicionalmente se utilizará una plantilla JSP para realizar el formateo de página en código HTML. El archivo listaProfesores.jsp se encarga de esta tarea y residirá en el directorio /tutorial2/root/ <%@ page import="java.util.*" %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Listado de profesores</h2> <% List profs = (List)request.getAttribute("profesores"); %> <table> <thead> <tr><th>Nombre</th><th>Título</th> <th>Area</th><th>Acciones</th></tr> </thead> 22 Capítulo 1. Contenidos Desarrollo con Java, Publicación <tbody> <% for(int i = 0, n = profs.size(); i < n; i++) { Map prof = (Map) profs.get(i); %> <tr><td><%= prof.get("nombre") %></td> <td><%= prof.get("titulo") %></td> <td><%= prof.get("area") %></td> <td> <a href='/domain.DetalleProfesor?id=<%= prof.get("id") %> <input type="submit" value="Detalle"/></a> <a href='/domain.EliminarProfesor?id=<%= prof.get("id") % <input type="submit" value="Eliminar"/></a></td></tr> <% } %> </tbody> <tfoot> <tr><td><a href='/domain.AgregarProfesor'> <input type="submit" name="action" value="Agregar </td><td></td><td></td><td></td></tr> </tfoot> </table> </html> Nótese que la tabla generada cuenta con enlaces que invocarán la rutina que presenta el detalle del profesor (que se describe a continuación). Hoja de estilo Con el fin de mejorar la apariencia de este ejemplo se ha desarrollado una pequeña hoja de estilo (css) que permite organizar mejor los datos de la tabla y el formulario. El archivo llamado style.css cuenta con el siguiente código: body { line-height: 1.6em; font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; color: #009; } table { font-size: 12px; 1.2. Rutinas de Transacción 23 Desarrollo con Java, Publicación margin: 45px; width: 480px; text-align: left; border-collapse: collapse; } th, tfoot td { font-size: 13px; font-weight: normal; padding: 8px; background: #b9c9fe; border-top: 4px solid #aabcfe; border-bottom: 1px solid #fff; color: #039; text-align: center; } td { padding: 8px; background: #e8edff; border-bottom: 1px solid #fff; color: #669; border-top: 1px solid transparent; } tr:hover td { background: #d0dafd; color: #339; } input[type="text"] { width: 250px; } La rutina de detalle La rutina de detalle de profesor presentará otra tabla HTML con la información detallada del profesor. Este archivo es llamado DetalleProfesor.java y se ubica en el mismo directorio /tutorial2/src/domain/. Es importante observar la utilización del id del profesor para realizar la consulta a la pasarela de datos. 24 Capítulo 1. Contenidos Desarrollo con Java, Publicación package domain; import display.FrontCommand; import data.ProfesorFinder; import data.ProfesorRowGateway; import import import import java.util.Map; java.util.HashMap; java.io.IOException; javax.servlet.ServletException; public class DetalleProfesor extends FrontCommand { public void process() throws ServletException, IOException { ProfesorFinder profs = (ProfesorFinder) context.getBean("profesorFinder" String id = request.getParameter("id"); ProfesorRowGateway prof = profs.find(id); Map params = new HashMap(); params.put("id",prof.getId()+""); params.put("cedula",prof.getCedula()); params.put("nombre",prof.getNombre()); params.put("titulo",prof.getTitulo()); params.put("area",prof.getArea()); params.put("telefono",prof.getTelefono()); request.setAttribute("profesor",params); forward("/detalleProfesor.jsp"); } } Plantilla JSP La plantilla JSP que genera el código HTML se presenta a continuación. El código de la plantilla se define en un archivo llamado detalleProfesor.jsp que reside también en el directorio /tutorial2/root/. 1.2. Rutinas de Transacción 25 Desarrollo con Java, Publicación <%@ page import="java.util.Map" %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Detalle de Profesor</h2> <% Map prof = (Map)request.getAttribute("profesor"); %> <form name="ActualizarProfesor" action="/domain.ActualizarProfesor" method="get"> <input type="hidden" name="id" value="<%= prof.get("id") %>"/> <table style="width:400px;"> <thead> <tr><th></th><th></th></tr> </thead> <tbody> <tr><td>Nombre:</td><td><input type="text" name="nombre" value="<%= prof.get("nombre") %>"/></td></tr> <tr><td>Cédula:</td><td><input type="text" name="cedula" value="<%= prof.get("cedula") %>"/></td></tr> <tr><td>Título:</td><td><input type="text" name="titulo" value="<%= prof.get("titulo") %>"/></td></tr> <tr><td>Area:</td><td><input type="text" name="area" value="<%= prof.get("area") %>"/></td></tr> <tr><td>Teléfono:</td><td><input type="text" name="telefo value="<%= prof.get("telefono") %>"/></td></tr> </tbody> <tfoot> <tr><td><input type="submit" value="Actualizar" /></td><td> </tfoot> </tbody> </table> </form> </html> 26 Capítulo 1. Contenidos Desarrollo con Java, Publicación Actualizar información Se presenta también la rutina de transacción que permite actualizar los datos de un profesor. La lógica de esta rutina se ubica en el archivo ActualizarProfesor.java y reside en el directorio /tutorial2/src/domain/. package domain; import display.FrontCommand; import data.ProfesorFinder; import data.ProfesorRowGateway; import import import import java.util.Map; java.util.HashMap; java.io.IOException; javax.servlet.ServletException; public class ActualizarProfesor extends FrontCommand { public void process() throws ServletException, IOException { ProfesorFinder profs = (ProfesorFinder) context.getBean("profesorFinder" String id = request.getParameter("id"); ProfesorRowGateway prof = profs.find(id); if (prof!=null) { String cedula = request.getParameter("cedula"); if (cedula!=null) prof.setCedula(cedula); String nombre = request.getParameter("nombre"); if (nombre!=null) prof.setNombre(nombre); String titulo = request.getParameter("titulo"); if (titulo!=null) prof.setTitulo(titulo); String area = request.getParameter("area"); if (area!=null) prof.setArea(area); String telefono = request.getParameter("telefono"); if (telefono!=null) prof.setTelefono(telefono); prof.update(); } response.sendRedirect("domain.ListaProfesores"); } 1.2. Rutinas de Transacción 27 Desarrollo con Java, Publicación } Compilando la capa de dominio Se puede realizar la compilación de estas clases de dominio en forma separada del resto del código de la aplicación. Para ello es necesario contar con las librerías del framework Spring 3 (como se indicó antes) y con la librería servlet-api.jar ubicadas en el directorio /tutorial2/root/WEB-INF/lib/. La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDomainLayer.bat residente en el directorio /tutorial2 (todo en una sola línea): javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/domain/ListaProfesores.java src/domain/DetalleProfesor.java src/domain/ActualizarProfesor 1.2.4 Configuración del contexto El framework Spring permite crear archivos xml que definen la configuración del contexto de ejecución de la aplicación. El archivo de configuración llamado context.xml se deberá ubicar en el directorio /tutorial2/root/WEB-INF/ y contendrá la siguiente información. <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/cont xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/sprin http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spr <bean id="profesorFinder" class="data.ProfesorFinder"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.Basi destroy-method="close"> 28 Capítulo 1. Contenidos Desarrollo con Java, Publicación <property <property <property <property name="driverClassName" value="${jdbc.dr name="url" value="${jdbc.url}"/> name="username" value="${jdbc.username} name="password" value="${jdbc.password} </bean> <context:property-placeholder location="WEB-INF/jdbc.prop </beans> Los aspectos importantes que se pueden observar en este archivo son la declaración de una instancia (singleton) al constructor de pasarelas y la declaración de la fuente de datos JDBC. Precisamente para configurar la fuente de datos se utilizará un archivo de propiedades llamado jdbc.properties y que residirá en el directorio /tutorial2/root/WEB-INF. Su contenido es simplemente el siguiente: jdbc.driverClassName=org.sqlite.JDBC jdbc.url=jdbc:sqlite:database/universidad.db jdbc.username=sa jdbc.password=root Sin embargo, note que una base de datos SQLite no requiere indicar el usuario y su clave. 1.2.5 Configuración del servidor El servidor de servlets requiere del archivo de configuración de la aplicación para conocer en donde se ubica la clase a ejecutar. Además este archivo permite indicar la ubicación y nombre del archivo de contexto. Estos archivos de configuración del servlet siempre se llaman web.xml y deben residir en el directorio /tutorial2/root/WEBINF/. Para este caso su contenido sería el siguiente: <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> 1.2. Rutinas de Transacción 29 Desarrollo con Java, Publicación <display-name>Sistema Universitario</display-name> <description>Ejemplo de Rutinas de Transaccion</description> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/context.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <filter> <filter-name>UrlRewriteFilter</filter-name> <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewrit </filter> <filter-mapping> <filter-name>UrlRewriteFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>FrontController</servlet-name> <servlet-class>display.FrontController</servlet-class> </servlet> <servlet-mapping> <servlet-name>FrontController</servlet-name> <url-pattern>/frontController</url-pattern> </servlet-mapping> </web-app> Como se puede observar en este archivo, se utilizará una librería especial que permite redireccionar las solicitudes que se hacen al servicio con base en su URL. Para esto es necesario contar con un archivo urlrewrite.xml que se muestra a continuación y que residirá en el mismo directorio /tutorial2/root/WEB-INF/ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 2.6//E "http://tuckey.org/res/dtds/urlrewrite2.6.dtd"> <urlrewrite> <rule> 30 Capítulo 1. Contenidos Desarrollo con Java, Publicación <from>/*.css</from> <to>.css</to> </rule> <rule> <from>/(.*)$</from> <to>/frontController</to> <set name="command">$1</set> </rule> </urlrewrite> 1.2.6 Ejecución del tutorial Este ejemplo se puede ejecutar bajo cualquier contenedor de Servlet. Por ejemplo, para realizar la ejecución de pruebas se puede utilizar un producto como el Winstone Servlet Container que permite ejecutar servlets de forma muy sencilla. Se debe descargar el programa winstone-0.9.10.jar y copiarlo en el directorio /tutorial2/. Sin embargo, para lograr que Winstone ejecute plantillas JSP es necesario descargar algunas librerías adicionales que deben ser copiadas en el directorio /tutorial2/lib: el-api-6.0.18.jar jasper-6.0.18.jar jasper-el-6.0.18.jar jasper-jdt-6.0.18.jar jsp-api-6.0.18.jar jstl-api-1.2.jar jstl-impl-1.2.jar jtds-1.2.4.jar juli-6.0.18.jar servlet-api-2.5.jar servlet-api.jar 1.2. Rutinas de Transacción 31 Desarrollo con Java, Publicación urlrewrite-3.2.0.jar Para ejecutar el servidor de servlets se puede crear un archivo de instrucciones, llamado run.bat, similar al siguiente en el directorio /tutorial2/ (todo en una sola línea): java -jar winstone-0.9.10.jar --httpPort=8089 --commonLibFolder=l --useJasper=true --webroot=root Luego se puede acceder a la aplicación desde cualquier visualizador web y apuntando a la dirección http://localhost:8089/domain.ListaProfesores 1.3 Módulo de Tabla A continuación se presenta el mismo ejemplo del tutorial anterior de una aplicación Web de un Sistema Universitario. En este ejemplo se combinarán otros tres patrones descritos por Fowler: módulo de tabla para la lógica del dominio, pasarela a tabla de datos para la capa de acceso a los datos, y controlador de página para la capa de presentación. 1.3.1 Capa de acceso a datos La capa de acceso a datos utilizará una pasarela a tabla de datos que se conectará a una base de datos SQLite. Base de datos Se utilizará la misma base de datos SQLite del tutorial anterior para administrar los datos. Dicha base de datos debe llevar por nombre universidad.db y debe estar ubicada en el directorio /tutorial3/database/. El código SQL utilizado para generar la tabla de profesores sería el siguiente: 32 Capítulo 1. Contenidos Desarrollo con Java, Publicación CREATE TABLE profesor (id INTEGER PRIMARY KEY, cedula VARCHAR, nombre VARCHAR, titulo VARCHAR, area VARCHAR, telefono VARCHAR) INSERT INTO profesor VALUES(1,'101110111','Carlos Perez', 'Licenciado','Administracion','3456-7890'); INSERT INTO profesor VALUES(2,'202220222','Luis Torres', 'Master','Economia','6677-3456'); INSERT INTO profesor VALUES(3,'303330333','Juan Castro', 'Licenciado','Matematica','6755-7788'); Para administrar una base de datos SQLite se puede utilizar alguno de los excelentes productos creados para ello, tal como SQLiteman ó el plugin para Firefox llamado SQLite Manager Pasarela a tabla de datos Para implementar la capa de acceso de datos se utiliza una pasarela a tabla de datos. Para ello es necesario crear una clase abstracta que se encargue de almacenar la conexión jdbc. Esta clase se llamará TableGateway.java y residirá en el directorio /tutorial3/src/data/. package data; import java.util.*; import javax.sql.*; import org.springframework.jdbc.core.JdbcTemplate; public abstract class TableGateway { protected JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } } La otra clase necesaria es la que implementa la tabla de datos para los profesores. Este archivo se llama ProfesorGateway.java y reside en el mismo directorio /tutorial3/src/data/. 1.3. Módulo de Tabla 33 Desarrollo con Java, Publicación package data; import java.util.*; import javax.sql.*; import org.springframework.jdbc.core.JdbcTemplate; public class ProfesorGateway extends TableGateway { private final static String findStatement = "SELECT * "+ "FROM profesor "+ "WHERE id = ?"; public Map<String, Object> find(String id) { List profs = jdbcTemplate.queryForList(findStatement,id); return (Map<String, Object>)profs.get(0); } private final static String findAllStatement = "SELECT * "+ "FROM profesor "; public List<Map<String, Object>> findAll() { return jdbcTemplate.queryForList(findAllStatement); } private static final String insertStatement = "INSERT INTO profesor "+ "VALUES (?,?,?,?,?,?)"; public int insert(String cedula,String nombre,String titulo, String area, String telefono) { Random generator = new Random(); int id = generator.nextInt(); jdbcTemplate.update(insertStatement, id,cedula,nombre,titulo,area,telefono); return id; } private static final String updateStatement = 34 Capítulo 1. Contenidos Desarrollo con Java, Publicación "UPDATE profesor "+ "SET cedula = ?, nombre = ?, titulo = ?, "+ "area = ?, telefono = ? WHERE id = ?"; public void update(int id,String cedula,String nombre, String titulo, String area, String telefono) { jdbcTemplate.update(updateStatement, cedula,nombre,titulo,area,telefono,id); } private static final String deleteStatement = "DELETE FROM profesor "+ "WHERE id = ?"; public void delete(int id) { jdbcTemplate.update(deleteStatement,id); } } Compilando la capa de datos Se puede realizar la compilación de estas dos clases en forma separada del resto del código. Para ello es necesario contar con el framework Spring 3 el cual se debe descargar y copiar todas las librerías .jar del directorio lib de dicho framework hacia el directorio /tutorial3/root/WEB-INF/lib/. También es necesario contar en dicho directorio con el driver jdbc para SQLite y las librerías commons para manejo de conexiones a base de datos. Específicamente las librerías que deben residir en el directorio /tutorial3/root/WEB-INF/lib/ son las siguientes: commons-dbcp-1.4.jar commons-logging-1.1.1.jar commons-pool-1.6.jar servlet-api.jar spring-asm-3.2.0.M1.jar 1.3. Módulo de Tabla 35 Desarrollo con Java, Publicación spring-beans-3.2.0.M1.jar spring-context-3.2.0.M1.jar spring-core-3.2.0.M1.jar spring-expression-3.2.0.M1.jar spring-jdbc-3.2.0.M1.jar spring-tx.3.2.0.M1.jar spring-web-3.2.0.M1.jar sqlite-jdbc-3.5.9.jar La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileData.bat residente en el directorio /tutorial3 (todo en una sola línea): javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/data/TableGateway.java src/data/ProfesorGateway.java Nota: La versión del JDK debe ser superior a 6.0 1.3.2 La capa de lógica del dominio Para implementar la capa de lógica del dominio se utilizará la técnica de módulo de tabla. En este caso el módulo agrupa toda la lógica del dominio, pero no se encarga del acceso a datos. Para acceder a los datos se utiliza la pasarela a tabla de datos mostrada anteriormente. La única clase necesaria sería la llamada ProfesorModule.java y residirá en el directorio /tutorial3/src/domain/. package domain; import data.TableGateway; import data.ProfesorGateway; import java.util.Map; import java.util.List; 36 Capítulo 1. Contenidos Desarrollo con Java, Publicación import java.io.IOException; import javax.servlet.ServletException; public class ProfesorModule { private ProfesorGateway gateway; public void setGateway(TableGateway gateway) { this.gateway = (ProfesorGateway)gateway; } public void actualizar(int id, String cedula, String nombre, String titulo, String area, String telefono) throws Exception if (id <= 0) throw new Exception("Identificador de profesor incorrecto") if (titulo.toLowerCase().equals("bachiller") || titulo.toLowerCase().equals("licenciado") || titulo.toLowerCase().equals("master") || titulo.toLowerCase().equals("doctor")) gateway.update(id,cedula,nombre,titulo,area,telefono); else throw new Exception("Error en título de profesor"); } public Map<String,Object> buscar(int id) throws Exception { if (id <= 0) throw new Exception("Identificador de profesor incorrecto") Map<String,Object> prof = gateway.find(id+""); return prof; } public List<Map<String,Object>> listado() throws Exception { List<Map<String,Object>> profs = gateway.findAll(); return profs; } } 1.3. Módulo de Tabla 37 Desarrollo con Java, Publicación Compilando la capa de dominio Para compilar la clase del dominio es necesario contar con las librerías del framework Spring 3 (como se indicó antes). La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDomain.bat residente en el directorio /tutorial3/ (todo en una sola línea): javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/domain/ProfesorModule.java 1.3.3 Capa de presentación El servicio de la root ha sido implementado mediante controladores de página, en donde cada página se implementa como un controlador individual. La clase general para definir los controladores se llama PageController.java y debe residir en el directorio /tutorial3/src/display/. package display; import import import import java.io.*; java.util.*; javax.servlet.*; javax.servlet.http.*; import org.springframework.web.context.*; import org.springframework.web.context.support.*; public class PageController extends HttpServlet { protected WebApplicationContext context; public void init(ServletConfig config) throws ServletException super.init(config); context = WebApplicationContextUtils.getWebApplicationContext(getServ } 38 Capítulo 1. Contenidos Desarrollo con Java, Publicación protected void forward(String target, HttpServletRequest reque HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher = context.getServletContext().getRequestDispatcher(target); dispatcher.forward(request,response); } } El controlador de listado de profesores El primer controlador de página es el que permite mostrar el listado de profesores. Este archivo se llama ListaProfesores.java y reside en el mismo directorio /tutorial3/src/display/. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; import domain.ProfesorModule; public class ListaProfesores extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorModule module = (ProfesorModule) context.getBean("profesorModule"); try { List data = module.listado(); request.setAttribute("profesores",data); forward("/listaProfesores.jsp",request,response); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } 1.3. Módulo de Tabla 39 Desarrollo con Java, Publicación } } La plantilla JSP Adicionalmente se utilizará, con en el tutorial anterior, una plantilla JSP para realizar el formateo de página en código HTML. El archivo listaProfesores.jsp se encarga de esta tarea y residirá en el directorio /tutorial3/root/. <%@ page import="java.util.*" %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <h1>Sistema Universitario</h1> <h2>Listado de profesores</h2> <% List profs = (List)request.getAttribute("profesores"); %> <table> <thead> <tr><th>Nombre</th><th>Título</th><th>Area</th> <th>Acciones</th></tr> </thead> <tbody> <% for(int i = 0, n = profs.size(); i < n; i++) { Map prof = (Map) profs.get(i); %> <tr><td><%= prof.get("nombre") %></td> <td><%= prof.get("titulo") %></td> <td><%= prof.get("area") %></td> <td><a href='/detalleProfesor?id=<%= prof.get("id") %>'> <input type="submit" value="Detalle"/></a> <a href='/eliminarProfesor?id=<%= prof.get("id") %>'> <input type="submit" value="Eliminar"/></a></td></t <% } %> </tbody> <tfoot> 40 Capítulo 1. Contenidos Desarrollo con Java, Publicación <tr><td><a href='/agregarProfesor'> <input type="submit" name="action" value="Agregar"/></a> </td><td></td><td></td><td></td></tr> </tfoot> </table> </html> Nótese que la tabla generada cuenta con enlaces que invocarán la rutina que presenta el detalle del profesor (que se describe a continuación). Hoja de estilo Con el fin de mejorar la apariencia de este ejemplo se ha desarrollado una pequeña hoja de estilo (css) que permite organizar mejor los datos de la tabla y el formulario. El archivo llamado style.css cuenta con el siguiente código y y reside en el directorio /tutorial3/root/.: body { line-height: 1.6em; font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; color: #009; } table { font-size: 12px; margin: 45px; width: 480px; text-align: left; border-collapse: collapse; } th, tfoot td { font-size: 13px; font-weight: normal; padding: 8px; background: #b9c9fe; border-top: 4px solid #aabcfe; border-bottom: 1px solid #fff; color: #039; text-align: center; 1.3. Módulo de Tabla 41 Desarrollo con Java, Publicación } td { padding: 8px; background: #e8edff; border-bottom: 1px solid #fff; color: #669; border-top: 1px solid transparent; } tr:hover td { background: #d0dafd; color: #339; } input[type="text"] { width: 250px; } El controlador de detalle de profesor El controlador de detalle de profesor presentará otra tabla HTML con la información detallada del profesor. Este controlador es llamado DetalleProfesor.java y se ubica en el mismo directorio /tutorial3/src/display/. Es importante observar la utilización del id del profesor para realizar la consulta al módula de tabla. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; import domain.ProfesorModule; public class DetalleProfesor extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorModule module = 42 Capítulo 1. Contenidos Desarrollo con Java, Publicación (ProfesorModule) context.getBean("profesorModule"); try { String id = request.getParameter("id"); int idProf = Integer.parseInt(id); Map prof = module.buscar(idProf); request.setAttribute("profesor",prof); forward("/detalleProfesor.jsp",request,response); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } } Plantilla JSP La plantilla JSP que genera el código HTML del detalle del profesor, se presenta a continuación. El código de la plantilla se define en un archivo llamado detalleProfesor.jsp que reside también en el directorio /tutorial3/root/. <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=U <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Detalle de Profesor</h2> <% Map prof = (Map)request.getAttribute("profesor"); %> <form name="ActualizarProfesor" action="/actualizarProfesor" me <input type="hidden" name="id" value="<%= prof.get("id") %>"/> <table style="width:400px;"> <thead> <tr><th></th><th></th></tr> </thead> <tbody> <tr><td>Nombre:</td><td><input type="text" name="nombre" value="<%= prof.get("nombre") %>"/></td></tr> 1.3. Módulo de Tabla 43 Desarrollo con Java, Publicación <tr><td>Cédula:</td><td><input type="text" name="cedul value="<%= prof.get("cedula") %>"/></td></tr> <tr><td>Título:</td><td><input type="text" name="titul value="<%= prof.get("titulo") %>"/></td></tr> <tr><td>Area:</td><td><input type="text" name="area" value="<%= prof.get("area") %>"/></td></tr> <tr><td>Teléfono:</td><td><input type="text" name="tel value="<%= prof.get("telefono") %>"/></td></tr> </tbody> <tfoot> <tr><td><input type="submit" value="Actualizar" /> </td><td></td></tr> </tfoot> </table> </form> </html> El controlador para actualizar información Se presenta también el controlador de página que permite actualizar los datos de un profesor. La lógica de este controlador se ubica en el archivo ActualizarProfesor.java y reside en el directorio /tutorial3/src/display/. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; import domain.ProfesorModule; public class ActualizarProfesor extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorModule module = 44 Capítulo 1. Contenidos Desarrollo con Java, Publicación (ProfesorModule) context.getBean("profesorModule"); try { String id = request.getParameter("id"); int idProf = Integer.parseInt(id); String cedula = request.getParameter("cedula"); String nombre = request.getParameter("nombre"); String titulo = request.getParameter("titulo"); String area = request.getParameter("area"); String telefono = request.getParameter("telefono"); module.actualizar(idProf,cedula,nombre,titulo,area,telefono response.sendRedirect("listaProfesores"); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } } Compilando la capa de presentación Para compilar la capa de presentación es necesario contar con las librerías del framework Spring 3 (como se indicó antes) y con la librería servlet-api.jar ubicadas en el directorio /tutorial3/root/WEB-INF/lib/. La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDisplay.bat residente en el directorio /tutorial3/ (todo en una sola línea): javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/display/PageController.java src/display/ListaProfesores.java src/display/ActualizarProfesor src/display/DetalleProfesor.java 1.3.4 Configuración del contexto El framework Spring permite crear archivos xml que definen la configuración del contexto de ejecución de la aplicación. El archivo de 1.3. Módulo de Tabla 45 Desarrollo con Java, Publicación configuración llamado context.xml se deberá ubicar en el directorio /tutorial3/root/WEB-INF/ y contendrá la siguiente información. <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beanshttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-cont <bean id="profesorGateway" class="data.ProfesorGateway"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="profesorModule" class="domain.ProfesorModule"> <property name="gateway" ref="profesorGateway"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDat destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClas <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="WEB-INF/jdbc.properti </beans> Los aspectos importantes que se pueden observar en este archivo son la declaración de una instancia (singleton) al constructor de pasarelas y la declaración de la fuente de datos JDBC. Precisamente para configurar la fuente de datos se utilizará un archivo de propiedades llamado jdbc.properties y que residirá en el directorio /tutorial3/root/WEB-INF. Su contenido es simplemente el siguiente: jdbc.driverClassName=org.sqlite.JDBC jdbc.url=jdbc:sqlite:database/universidad.db jdbc.username=sa jdbc.password=root 46 Capítulo 1. Contenidos Desarrollo con Java, Publicación Sin embargo, note que una base de datos SQLite no requiere indicar el usuario y su clave. 1.3.5 Configuración del servidor El servidor de servlets requiere del archivo de configuración de la aplicación para conocer en donde se ubica la clase a ejecutar. Además este archivo permite indicar la ubicación y nombre del archivo de contexto. Estos archivos de configuración del servlet siempre se llaman web.xml y deben residir en el directorio /tutorial3/root/WEBINF/. Para este caso su contenido sería el siguiente: <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_ version="2.4"> <display-name>Sistema Universitario</display-name> <description>Ejemplo de Módulo de Tabla</description> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/context.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>ActualizarProfesor</servlet-name> <servlet-class>display.ActualizarProfesor</servlet-class> </servlet> <servlet> <servlet-name>DetalleProfesor</servlet-name> <servlet-class>display.DetalleProfesor</servlet-class> </servlet> <servlet> <servlet-name>ListaProfesores</servlet-name> 1.3. Módulo de Tabla 47 Desarrollo con Java, Publicación <servlet-class>display.ListaProfesores</servlet-class> </servlet> <servlet-mapping> <servlet-name>ActualizarProfesor</servlet-name> <url-pattern>/actualizarProfesor</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>DetalleProfesor</servlet-name> <url-pattern>/detalleProfesor</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListaProfesores</servlet-name> <url-pattern>/listaProfesores</url-pattern> </servlet-mapping> </web-app> 1.3.6 Ejecución del tutorial Este ejemplo se puede ejecutar bajo cualquier contenedor de Servlet. Por ejemplo, para realizar la ejecución de pruebas se puede utilizar un producto como el Winstone Servlet Container que permite ejecutar servlets de forma muy sencilla. Se debe descargar el programa winstone-0.9.10.jar y copiarlo en el directorio /tutorial3/. Sin embargo, para lograr que Winstone ejecute plantillas JSP es necesario descargar algunas librerías adicionales que deben ser copiadas en el directorio /tutorial3/lib: el-api-6.0.18.jar jasper-6.0.18.jar jasper-el-6.0.18.jar jasper-jdt-6.0.18.jar jsp-api-6.0.18.jar jstl-api-1.2.jar jstl-impl-1.2.jar 48 Capítulo 1. Contenidos Desarrollo con Java, Publicación jtds-1.2.4.jar juli-6.0.18.jar servlet-api-2.5.jar servlet-api.jar urlrewrite-3.2.0.jar Para ejecutar el servidor de servlets se puede crear un archivo de instrucciones, llamado run.bat, similar al siguiente en el directorio /tutorial3/ : java -jar winstone-0.9.10.jar --httpPort=8089 --commonLibFolder=l --useJasper=true --webroot=root Luego se puede acceder a la aplicación desde cualquier visualizador web y apuntando a la dirección http://localhost:8089/listaProfesores 1.4 Modelo del Dominio A continuación se presenta el ejemplo de la aplicación Web de un Sistema Universitario pero en este caso utilizando un modelo del Dominio. Específicamente, en este ejemplo se combinarán otros patrones descritos por Fowler: modelo del dominio para la lógica del dominio, y controlador de página para la capa de presentación. 1.4.1 La capa de lógica del dominio Para implementar la capa de lógica del dominio se utilizará la técnica de modelo del dominio. En este caso el modelo agrupa toda la lógica del dominio, pero no se encarga del acceso a datos. La primer clase necesaria consiste en la entidad profesor Profesor.java y residirá en el directorio /tutorial4/src/domain/. Esta clase es la que contendría la lógica del dominio. 1.4. Modelo del Dominio 49 Desarrollo con Java, Publicación package domain; public class Profesor { private int id; private String cedula; private String nombre; private String titulo; private String area; private String telefono; public Profesor () {}; public void setId(int id) {this.id=id;} public void setCedula(String cedula) {this.cedula=cedula;} public void setNombre(String nombre) {this.nombre=nombre;} public void setTitulo(String titulo) throws Exception { if (titulo.toLowerCase().equals("bachiller") || titulo.toLowerCase().equals("licenciado") || titulo.toLowerCase().equals("master") || titulo.toLowerCase().equals("doctor")) this.titulo=titulo; else throw new Exception("Error en título de profesor"); } public void setArea(String area) {this.area=area;} public void setTelefono(String telefono) {this.telefono=telefon public public public public public public int getId() {return id;} String getCedula() {return cedula;} String getNombre() {return nombre;} String getTitulo() {return titulo;} String getArea() {return area;} String getTelefono() {return telefono;} } El repositorio de datos Para mantener almacenados y recuperar los diferentes objetos, se utilizará un objeto llamado ProfesorRepository.java residente en el mismo directorio /tutorial4/src/domain/. Esta clase mantendrá los datos 50 Capítulo 1. Contenidos Desarrollo con Java, Publicación de los objetos en memoria. Por tanto no será necesario utilizar una base de datos en este tutorial. package domain; import java.util.Map; import java.util.HashMap; import java.util.Collection; public class ProfesorRepository { private Map<String,Profesor> profesores; ProfesorRepository() { profesores = new HashMap<String,Profesor>(); } public boolean insertProfesor(Profesor prof) { if (profesores.containsKey(prof.getId())) return false; else profesores.put(prof.getId()+"",prof); return true; } public boolean deleteProfesor(Profesor prof) { if (!profesores.containsKey(prof.getId())) return false; else profesores.remove(prof.getId()); return true; } public Profesor findProfesor(String id) { if (!profesores.containsKey(id)) return null; else return profesores.get(id); } public boolean updateProfesor(Profesor prof) { if (!profesores.containsKey(prof.getId()+"")) return false; else profesores.put(prof.getId()+"",prof); return true; 1.4. Modelo del Dominio 51 Desarrollo con Java, Publicación } public Collection findAllProfesor() { return profesores.values(); } public void setProfesores(Map profesores) { this.profesores = profesores; } } La fábrica de objetos Generalmente cuando se elabora un modelo del dominio es importante crear una clase aparte que se encargue de crear instancias de objetos. En este caso se utilizará la clase ProfesorFactory.java y se ubicará en el mismo directorio /tutorial4/src/domain/ package domain; public class ProfesorFactory { public Profesor Create(int id,String cedula,String nombre, String titulo,String area,String telefono) { try { Profesor prof = new Profesor(); prof.setId(id); prof.setCedula(cedula); prof.setNombre(nombre); prof.setTitulo(titulo); prof.setArea(area); prof.setTelefono(telefono); return prof; } catch (Exception e) { return null; } } } 52 Capítulo 1. Contenidos Desarrollo con Java, Publicación Compilando la capa del dominio Se puede realizar la compilación de estas tres clases en forma separada del resto del código. La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDomainLayer.bat residente en el directorio /tutorial4/ (todo en una sola línea): javac -d root/WEB-INF/classes src/domain/Profesor.java src/domain/ProfesorFactory.java src/domain/ProfesorRepository.j Nota: La versión del JDK debe ser superior a 6.0 1.4.2 Capa de presentación El servicio de la universidad ha será implementado mediante controladores de página, en donde cada página se implementa como un controlador individual. La clase general para definir los controladores se llama PageController.java y debe residir en el directorio /tutorial4/src/display/. package display; import import import import java.io.*; java.util.*; javax.servlet.*; javax.servlet.http.*; import org.springframework.web.context.*; import org.springframework.web.context.support.*; public class PageController extends HttpServlet { protected WebApplicationContext context; public void init(ServletConfig config) throws ServletException super.init(config); context = WebApplicationContextUtils.getWebApplicationContext(getServ } 1.4. Modelo del Dominio 53 Desarrollo con Java, Publicación protected void forward(String target, HttpServletRequest reque HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher = context.getServletContext().getRequestDispatcher(target); dispatcher.forward(request,response); } } El controlador de listado de profesores El primer controlador de página es el que permite mostrar el listado de profesores. Este archivo se llama ListaProfesores.java y reside en el mismo directorio /tutorial4/src/display/. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; import domain.ProfesorRepository; import domain.Profesor; public class ListaProfesores extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorRepository profesores = (ProfesorRepository) context.getBean("profesorRepository" try { Collection lista = profesores.findAllProfesor(); List data = new ArrayList(); Iterator itr = lista.iterator(); while (itr.hasNext()) { Profesor prof = (Profesor)itr.next(); 54 Capítulo 1. Contenidos Desarrollo con Java, Publicación ProfesorDTO dto = ProfesorAssembler.Create(prof); data.add(dto); } request.setAttribute("profesores",data); forward("/listaProfesores.jsp",request,response); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } } La plantilla JSP Adicionalmente se utilizará, con en el tutorial anterior, una plantilla JSP para realizar el formateo de página en código HTML. El archivo listaProfesores.jsp se encarga de esta tarea y residirá en el directorio /tutorial4/root/. <%@ page import="java.util.*" %> <%@ page import="display.*" %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> <meta http-equiv="Content-Type" content="text/html; charset=U </head> <h1>Sistema Universitario</h1> <h2>Listado de profesores</h2> <% List profs = (List)request.getAttribute("profesores"); %> <table> <thead> <tr><th>Nombre</th><th>Título</th><th>Area</th><th>Acc </thead> <tbody> <% for(int i = 0, n = profs.size(); i < n; i++) { ProfesorDTO prof = (ProfesorDTO) profs.get(i); %> <tr><td><%= prof.nombre %></td> <td><%= prof.titulo %></td> 1.4. Modelo del Dominio 55 Desarrollo con Java, Publicación <td><%= prof.area %></td> <td><a href='/detalleProfesor?id=<%= prof.id %>'> <input type="submit" value="Detalle"/></a> <a href='/eliminarProfesor?id=<%= prof.id %>'> <input type="submit" value="Eliminar"/></a></td></t <% } %> </tbody> <tfoot> <tr><td><a href='/agregarProfesor'> <input type="submit" name="action" value="Agregar"/></a> </td><td></td><td></td><td></td></tr> </tfoot> </table> </html> Nótese que la tabla generada cuenta con enlaces que invocarán la rutina que presenta el detalle del profesor (que se describe a continuación). Hoja de estilo Con el fin de mejorar la apariencia de este ejemplo se ha desarrollado una pequeña hoja de estilo (css) que permite organizar mejor los datos de la tabla y el formulario. El archivo llamado style.css cuenta con el siguiente código y y reside en el directorio /tutorial4/root/.: body { line-height: 1.6em; font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; color: #009; } table { font-size: 12px; margin: 45px; width: 480px; text-align: left; border-collapse: collapse; } th, tfoot td { 56 Capítulo 1. Contenidos Desarrollo con Java, Publicación font-size: 13px; font-weight: normal; padding: 8px; background: #b9c9fe; border-top: 4px solid #aabcfe; border-bottom: 1px solid #fff; color: #039; text-align: center; } td { padding: 8px; background: #e8edff; border-bottom: 1px solid #fff; color: #669; border-top: 1px solid transparent; } tr:hover td { background: #d0dafd; color: #339; } input[type="text"] { width: 250px; } El controlador de detalle de profesor El controlador de detalle de profesor presentará otra tabla HTML con la información detallada del profesor. Este controlador es llamado DetalleProfesor.java y se ubica en el mismo directorio /tutorial4/src/display. Es importante observar la utilización del id del profesor para realizar la consulta al módula de tabla. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; 1.4. Modelo del Dominio 57 Desarrollo con Java, Publicación import domain.ProfesorRepository; import domain.Profesor; public class DetalleProfesor extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorRepository profesores = (ProfesorRepository) context.getBean("profesorRepositor try { String id = request.getParameter("id"); int idProf = Integer.parseInt(id); Profesor prof = profesores.findProfesor(idProf+""); ProfesorDTO dto = ProfesorAssembler.Create(prof); request.setAttribute("profesor",dto); forward("/detalleProfesor.jsp",request,response); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } } Plantilla JSP La plantilla JSP que genera el código HTML del detalle del profesor, se presenta a continuación. El código de la plantilla se define en un archivo llamado detalleProfesor.jsp que reside también en el directorio /tutorial4/root/. <%@ page import="java.util.Map" %> <%@ page import="display.*" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=U <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> 58 Capítulo 1. Contenidos Desarrollo con Java, Publicación <h1>Sistema Universitario</h1> <h2>Detalle de Profesor</h2> <% ProfesorDTO prof = (ProfesorDTO)request.getAttribute("profes <form name="ActualizarProfesor" action="/actualizarProfesor" me <input type="hidden" name="id" value="<%= prof.id %>"/> <table> <thead> <tr><th></th><th></th></tr> </thead> <tbody> <tr><td>Nombre:</td><td><input type="text" name="nombre" value="<%= prof.nombre %>"/></td></tr> <tr><td>Cédula:</td><td><input type="text" name="cedul value="<%= prof.cedula %>"/></td></tr> <tr><td>Título:</td><td><input type="text" name="titul value="<%= prof.titulo %>"/></td></tr> <tr><td>Area:</td><td><input type="text" name="area" value="<%= prof.area %>"/></td></tr> <tr><td>Teléfono:</td><td><input type="text" name="tel value="<%= prof.telefono %>"/></td></tr> </tbody> <tfoot> <tr><td><input type="submit" value="Actualizar" /></td><td></ </tfoot> </table> </form> </html> El controlador para actualizar información Se presenta también el controlador de página que permite actualizar los datos de un profesor. La lógica de este controlador se ubica en el archivo ActualizarProfesor.java y reside en el directorio /tutorial4/src/display/. package display; import java.util.*; import java.io.*; import javax.servlet.*; 1.4. Modelo del Dominio 59 Desarrollo con Java, Publicación import javax.servlet.http.*; import org.springframework.web.context.*; import domain.ProfesorRepository; import domain.Profesor; public class ActualizarProfesor extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorRepository profesores = (ProfesorRepository) context.getBean("profesorRepository" try { String id = request.getParameter("id"); int idProf = Integer.parseInt(id); String cedula = request.getParameter("cedula"); String nombre = request.getParameter("nombre"); String titulo = request.getParameter("titulo"); String area = request.getParameter("area"); String telefono = request.getParameter("telefono"); Profesor prof = profesores.findProfesor(idProf+""); try { if (cedula!=null) prof.setCedula(cedula); if (nombre!=null) prof.setNombre(nombre); if (titulo!=null) prof.setTitulo(titulo); if (area!=null) prof.setArea(area); if (telefono!=null) prof.setTelefono(telefono); } catch (Exception e) {} response.sendRedirect("listaProfesores"); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } } 60 Capítulo 1. Contenidos Desarrollo con Java, Publicación El DTO de profesor En esta implementación se utiliza una clase tipo DTO (Data Transfer Object) que facilite el paso de información hacia las vistas de datos. Para ello se utiliza la clase ProfesorDTO.java residente en el directorio /tutorial4/src/display/. package display; public class ProfesorDTO { public int id; public String cedula; public String nombre; public String titulo; public String area; public String telefono; } El ensamblador del DTO Adicionalmente es necesario contar con una clase que realice el ensamblaje del DTO a partir de la entidad de profesor. Aquí se utiliza la clase ProfesorAssembler.java residente en el mismo directorio /tutorial4/src/display/. package display; import domain.Profesor; public class ProfesorAssembler { public static ProfesorDTO Create(Profesor prof) { ProfesorDTO dto = new ProfesorDTO(); dto.id = prof.getId(); dto.cedula = prof.getCedula(); dto.nombre = prof.getNombre(); dto.titulo = prof.getTitulo(); dto.area = prof.getArea(); dto.telefono = prof.getTelefono(); return dto; 1.4. Modelo del Dominio 61 Desarrollo con Java, Publicación } } Compilando la capa de presentación Para compilar la capa de presentación es necesario contar con las librerías del framework Spring 3 (como se indicó antes) y con la librería *servlet-api.jar** ubicadas en el directorio /tutorial4/root/WEBINF/lib/. Específicamente las librerías necesarias son las siguientes: servlet-api.jar spring-asm-3.2.0.M1.jar spring-beans-3.2.0.M1.jar spring-context-3.2.0.M1.jar spring-core-3.2.0.M1.jar spring-expression-3.2.0.M1.jar spring-jdbc-3.2.0.M1.jar spring-tx.3.2.0.M1.jar spring-web-3.2.0.M1.jar La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDisplayLayer.bat residente en el directorio /tutorial4/ (todo en una sola línea): javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/display/PageController.java src/display/ActualizarProfesor.java src/display/DetalleProfesor src/display/ListaProfesores.java src/display/ProfesorAssembler. src/display/ProfesorDTO.java 62 Capítulo 1. Contenidos Desarrollo con Java, Publicación 1.4.3 Configuración del contexto El framework Spring permite crear archivos xml que definen la configuración del contexto de ejecución de la aplicación. El archivo de configuración llamado context.xml se deberá ubicar en el directorio /tutorial4/root/WEB-INF/ y contendrá la siguiente información. <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context<bean id="profesorRepository" class="domain.ProfesorRepository" <property name="profesores"> <map> <entry> <key><value>1</value></key> <ref bean="P001" /> </entry> <entry> <key><value>2</value></key> <ref bean="P002" /> </entry> <entry> <key><value>3</value></key> <ref bean="P003" /> </entry> </map> </property> </bean> <bean id="P001" class="domain.Profesor"> <property name="id" value="1"/> <property name="cedula" value="101110111"/> <property name="nombre" value="Carlos Perez"/> <property name="titulo" value="Licenciado"/> <property name="area" value="Administracion"/> 1.4. Modelo del Dominio 63 Desarrollo con Java, Publicación <property name="telefono" value="3456-7890"/> </bean> <bean id="P002" class="domain.Profesor"> <property name="id" value="2"/> <property name="cedula" value="202220222"/> <property name="nombre" value="Luis Torres"/> <property name="titulo" value="Master"/> <property name="area" value="Economia"/> <property name="telefono" value="6677-3456"/> </bean> <bean id="P003" class="domain.Profesor"> <property name="id" value="3"/> <property name="cedula" value="303330333"/> <property name="nombre" value="Juan Castro"/> <property name="titulo" value="Licenciado"/> <property name="area" value="Matematica"/> <property name="telefono" value="6755-7788"/> </bean> </beans> Los aspectos importantes que se pueden observar en este archivo son la declaración de una instancia (singleton) del repositorio de profesores y la forma como se crean los objetos en forma dinámica. 1.4.4 Configuración del servidor El servidor de servlets requiere del archivo de configuración de la aplicación para conocer en donde se ubica la clase a ejecutar. Además este archivo permite indicar la ubicación y nombre del archivo de contexto. Estos archivos de configuración del servlet siempre se llaman web.xml y deben residir en el directorio /tutorial4/root/WEBINF/. Para este caso su contenido sería el siguiente: <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_ version="2.4"> 64 Capítulo 1. Contenidos Desarrollo con Java, Publicación <display-name>Sistema Universitario</display-name> <description>Ejemplo de Rutinas de Transaccion</description> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/context.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>ActualizarProfesor</servlet-name> <servlet-class>display.ActualizarProfesor</servlet-class> </servlet> <servlet> <servlet-name>DetalleProfesor</servlet-name> <servlet-class>display.DetalleProfesor</servlet-class> </servlet> <servlet> <servlet-name>ListaProfesores</servlet-name> <servlet-class>display.ListaProfesores</servlet-class> </servlet> <servlet-mapping> <servlet-name>ActualizarProfesor</servlet-name> <url-pattern>/root/actualizarProfesor</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>DetalleProfesor</servlet-name> <url-pattern>/root/detalleProfesor</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListaProfesores</servlet-name> <url-pattern>/root/listaProfesores</url-pattern> </servlet-mapping> </web-app> 1.4. Modelo del Dominio 65 Desarrollo con Java, Publicación 1.4.5 Ejecución del tutorial Este ejemplo se puede ejecutar bajo cualquier contenedor de Servlet. Por ejemplo, para realizar la ejecución de pruebas se puede utilizar un producto como el Winstone Servlet Container que permite ejecutar servlets de forma muy sencilla. Se debe descargar el programa winstone-0.9.10.jar y copiarlo en el directorio /tutorial4/. Sin embargo, para lograr que Winstone ejecute plantillas JSP es necesario descargar algunas librerías adicionales que deben ser copiadas en el directorio /tutorial4/lib: el-api-6.0.18.jar jasper-6.0.18.jar jasper-el-6.0.18.jar jasper-jdt-6.0.18.jar jsp-api-6.0.18.jar jstl-api-1.2.jar jstl-impl-1.2.jar jtds-1.2.4.jar juli-6.0.18.jar servlet-api-2.5.jar servlet-api.jar Para ejecutar el servidor de servlets se puede crear un archivo de instrucciones, llamado run.bat, similar al siguiente en el directorio /tutorial4/ (todo en una sola línea): java -jar winstone-0.9.10.jar --httpPort=8089 --commonLibFolder=l --useJasper=true --webroot=root Luego se puede acceder a la aplicación desde cualquier visualizador web y apuntando a la dirección http://localhost:8089/listaProfesores 66 Capítulo 1. Contenidos Desarrollo con Java, Publicación 1.5 Mapeo objeto/relacional Este tutorial muestra la forma de desarrollar una aplicación que utilice modelo del dominio como patrón de diseño de la capa de dominio, y mapeador objeto/relacional para la capa de acceso a datos. 1.5.1 Capa de lógica del dominio Para implementar la capa de lógica del dominio se utilizará la técnica de modelo del dominio tal como el tutorial anterior. En este caso el modelo agrupa toda la lógica del dominio, pero no se encarga del acceso a datos. La primer clase necesaria consiste en la entidad profesor Profesor.java y residirá en el directorio /tutorial5/src/domain/. Esta clase es la que contendría la lógica del dominio. package domain; public class Profesor { private int id; private String cedula; private String nombre; private String titulo; private String area; private String telefono; public Profesor () {}; public void setId(int id) {this.id=id;} public void setCedula(String cedula) {this.cedula=cedula;} public void setNombre(String nombre) {this.nombre=nombre;} public void setTitulo(String titulo) throws Exception { if (titulo.toLowerCase().equals("bachiller") || titulo.toLowerCase().equals("licenciado") || titulo.toLowerCase().equals("master") || titulo.toLowerCase().equals("doctor")) this.titulo=titulo; else 1.5. Mapeo objeto/relacional 67 Desarrollo con Java, Publicación throw new Exception("Error en título de profesor"); } public void setArea(String area) {this.area=area;} public void setTelefono(String telefono) {this.telefono=telefon public int getId() {return id;} public String getCedula() {return cedula;} public String getNombre() {return nombre;} public String getTitulo() {return titulo;} public String getArea() {return area;} public String getTelefono() {return telefono;} } El repositorio de datos Para mantener almacenados y recuperar los diferentes objetos, se utiliza el objeto llamado ProfesorRepository.java residente en el mismo directorio /tutorial5/src/domain/. Sin embargo, este tipo de objeto solamente es una interfase Java como se puede observar a continuación: package domain; import java.util.Map; import java.util.HashMap; import java.util.Collection; public interface ProfesorRepository { public boolean insertProfesor(Profesor prof); public boolean deleteProfesor(Profesor prof); public Profesor findProfesor(int id); public boolean updateProfesor(Profesor prof); public Collection findAllProfesor(); } La fábrica de objetos Generalmente cuando se elabora un modelo del dominio es importante crear una clase aparte que se encargue de crear instancias de 68 Capítulo 1. Contenidos Desarrollo con Java, Publicación objetos. En este caso se utilizará la clase ProfesorFactory.java y se ubicará en el mismo directorio /tutorial5/src/domain/ package domain; public class ProfesorFactory { public Profesor Create(int id,String cedula,String nombre, String titulo,String area,String telefono) { try { Profesor prof = new Profesor(); prof.setId(id); prof.setCedula(cedula); prof.setNombre(nombre); prof.setTitulo(titulo); prof.setArea(area); prof.setTelefono(telefono); return prof; } catch (Exception e) { return null; } } } Compilando la capa del dominio Se puede realizar la compilación de estas tres clases en forma separada del resto del código. La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDomainLayer.bat residente en el directorio /tutorial5/ (todo en una sola línea): javac -d root/WEB-INF/classes src/domain/Profesor.java src/domain/ProfesorFactory.java src/domain/ProfesorRepository.j Nota: La versión del JDK debe ser superior a 6.0 1.5. Mapeo objeto/relacional 69 Desarrollo con Java, Publicación 1.5.2 Capa de presentación El servicio de la universidad será implementado mediante controladores de página (tal como se hizo en el tutorial anterior), en donde cada página se implementa como un controlador individual. Igual que antes, la clase general para definir los controladores se llama PageController.java y debe residir en el directorio /tutorial5/src/display/. package display; import import import import java.io.*; java.util.*; javax.servlet.*; javax.servlet.http.*; import org.springframework.web.context.*; import org.springframework.web.context.support.*; public class PageController extends HttpServlet { protected WebApplicationContext context; public void init(ServletConfig config) throws ServletException super.init(config); context = WebApplicationContextUtils.getWebApplicationContext( getServletContext()); } protected void forward(String target, HttpServletRequest reque HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher = context.getServletContext().getRequestDispatcher(target); dispatcher.forward(request,response); } } 70 Capítulo 1. Contenidos Desarrollo con Java, Publicación El controlador de listado de profesores El primer controlador de página es el que permite mostrar el listado de profesores. Este archivo se llama ListaProfesores.java y reside en el mismo directorio /tutorial5/src/display/. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; import domain.ProfesorRepository; import domain.Profesor; import util.ProfesorDTO; import util.ProfesorAssembler; public class ListaProfesores extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorRepository profesores = (ProfesorRepository) context.getBean("profesorRepository" try { Collection lista = profesores.findAllProfesor(); List data = new ArrayList(); Iterator itr = lista.iterator(); while (itr.hasNext()) { Profesor prof = (Profesor)itr.next(); ProfesorDTO dto = ProfesorAssembler.CreateDTO(prof); data.add(dto); } request.setAttribute("profesores",data); forward("/listaProfesores.jsp",request,response); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } 1.5. Mapeo objeto/relacional 71 Desarrollo con Java, Publicación } La plantilla JSP Adicionalmente se utilizará, con en el tutorial anterior, una plantilla JSP para realizar el formateo de página en código HTML. El archivo listaProfesores.jsp se encarga de esta tarea y residirá en el directorio /tutorial5/root/. <%@ page import="java.util.*" %> <%@ page import="util.*" %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Listado de profesores</h2> <% List profs = (List)request.getAttribute("profesores"); %> <table> <thead> <tr><th>Nombre</th><th>Título</th> <th>Area</th><th>Acciones</th></tr> </thead> <tbody> <% for(int i = 0, n = profs.size(); i < n; i++) { ProfesorDTO prof = (ProfesorDTO) profs.get(i); %> <tr><td><%= prof.getNombre() %></td> <td><%= prof.getTitulo() %></td> <td><%= prof.getArea() %></td> <td><a href='/detalleProfesor?id=<%= prof.getId() %>'> <input type="submit" value="Detalle"/></a> <a href='/eliminarProfesor?id=<%= prof.getId() %>'> <input type="submit" value="Eliminar"/></a></td></t <% } %> </tbody> <tfoot> <tr><td><a href='/agregarProfesor'> <input type="submit" name="action" value="Agregar"/></a> 72 Capítulo 1. Contenidos Desarrollo con Java, Publicación </td><td></td><td></td><td></td></tr> </tfoot> </table> </html> Nótese que la tabla generada cuenta con enlaces que invocarán la rutina que presenta el detalle del profesor (que se describe a continuación). Hoja de estilo Con el fin de mejorar la apariencia de este ejemplo se ha desarrollado una pequeña hoja de estilo (css) que permite organizar mejor los datos de la tabla y el formulario. El archivo llamado style.css cuenta con el siguiente código y y reside en el directorio /tutorial5/root/.: body { line-height: 1.6em; font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; color: #009; } table { font-size: 12px; margin: 45px; width: 480px; text-align: left; border-collapse: collapse; } th, tfoot td { font-size: 13px; font-weight: normal; padding: 8px; background: #b9c9fe; border-top: 4px solid #aabcfe; border-bottom: 1px solid #fff; color: #039; text-align: center; } td { 1.5. Mapeo objeto/relacional 73 Desarrollo con Java, Publicación padding: 8px; background: #e8edff; border-bottom: 1px solid #fff; color: #669; border-top: 1px solid transparent; } tr:hover td { background: #d0dafd; color: #339; } input[type="text"] { width: 250px; } El controlador de detalle de profesor El controlador de detalle de profesor presentará otra tabla HTML con la información detallada del profesor. Este controlador es llamado DetalleProfesor.java y se ubica en el mismo directorio /tutorial5/src/display/. Es importante observar la utilización del id del profesor para realizar la consulta al módulo de tabla. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; import domain.ProfesorRepository; import domain.Profesor; import util.ProfesorDTO; import util.ProfesorAssembler; public class DetalleProfesor extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorRepository profesores = 74 Capítulo 1. Contenidos Desarrollo con Java, Publicación (ProfesorRepository) context.getBean("profesorRepository" try { String id = request.getParameter("id"); int idProf = Integer.parseInt(id); Profesor prof = profesores.findProfesor(idProf); ProfesorDTO dto = ProfesorAssembler.CreateDTO(prof); request.setAttribute("profesor",dto); forward("/detalleProfesor.jsp",request,response); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } } Plantilla JSP La plantilla JSP que genera el código HTML del detalle del profesor, se presenta a continuación. El código de la plantilla se define en un archivo llamado detalleProfesor.jsp que reside también en el directorio /tutorial5/root/. <%@ page import="java.util.Map" %> <%@ page import="util.*" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Detalle de Profesor</h2> <% ProfesorDTO prof = (ProfesorDTO)request.getAttribute("profesor"); %> <form name="ActualizarProfesor" action="/actualizarProfesor" method="get"> <input type="hidden" name="id" value="<%= prof.getId() %>"/> <table> 1.5. Mapeo objeto/relacional 75 Desarrollo con Java, Publicación <thead> <tr><th></th><th></th></tr> </thead> <tbody> <tr><td>Nombre:</td><td><input type="text" name="nombre" value="<%= prof.getNombre() %>"/></td></tr> <tr><td>Cédula:</td><td><input type="text" name="cedula" value="<%= prof.getCedula() %>"/> </td></tr> <tr><td>Título:</td><td><input type="text" name="titulo" value="<%= prof.getTitulo() %>"/> </td></tr> <tr><td>Area:</td><td><input type="text" name="area" value="<%= prof.getArea() %>"/></td></tr> <tr><td>Teléfono:</td><td><input type="text" name="telefono" value="<%= prof.getTelefono() %>"/> </td></tr> </tbody> <tfoot> <tr><td><input type="submit" value="Actualizar" /> </td><td></td></tr> </tfoot> </table> </form> </html> El controlador para actualizar información Se presenta también el controlador de página que permite actualizar los datos de un profesor. La lógica de este controlador se ubica en el archivo ActualizarProfesor.java y reside en el directorio /tutorial5/src/display/. package display; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.springframework.web.context.*; 76 Capítulo 1. Contenidos Desarrollo con Java, Publicación import domain.ProfesorRepository; import domain.Profesor; import util.ProfesorDTO; import util.ProfesorAssembler; public class ActualizarProfesor extends PageController { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProfesorRepository profesores = (ProfesorRepository) context.getBean("profesorRepository" try { String id = request.getParameter("id"); int idProf = Integer.parseInt(id); String cedula = request.getParameter("cedula"); String nombre = request.getParameter("nombre"); String titulo = request.getParameter("titulo"); String area = request.getParameter("area"); String telefono = request.getParameter("telefono"); Profesor prof = profesores.findProfesor(idProf); try { if (cedula!=null) prof.setCedula(cedula); if (nombre!=null) prof.setNombre(nombre); if (titulo!=null) prof.setTitulo(titulo); if (area!=null) prof.setArea(area); if (telefono!=null) prof.setTelefono(telefono); profesores.updateProfesor(prof); } catch (Exception e) {} response.sendRedirect("listaProfesores"); } catch (Exception e) { request.setAttribute("mensaje",e.getMessage()); forward("/paginaError.jsp",request,response); } } } 1.5. Mapeo objeto/relacional 77 Desarrollo con Java, Publicación Plantilla JSP de mensaje de error Se puede utilizar un archivo JSP adicional para desplegar los mensajes de error. En particular este archivo será llamado paginaError.jsp y residirá en el directorio /tutorial5/root/. <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=U <title>Sistema Universitario</title> </head> <% String mensaje = (String)request.getAttribute("mensaje"); %> <h1>Error en operación</h1> <p><%= mensaje %></p> </html> El DTO de profesor En esta implementación también se utiliza una clase tipo DTO (Data Transfer Object) que facilite el paso de información hacia las vistas de datos. Para ello se utiliza la clase ProfesorDTO.java pero esta clase residirá en el directorio /tutorial5/src/util/. package util; public class ProfesorDTO { private int id; private String cedula; private String nombre; private String titulo; private String area; private String telefono; public public public public public public 78 int getId() {return id;} String getCedula() {return cedula;} String getNombre() {return nombre;} String getTitulo() {return titulo;} String getArea() {return area;} String getTelefono() {return telefono;} Capítulo 1. Contenidos Desarrollo con Java, Publicación public public public public public public void void void void void void setId(int id) {this.id=id;} setCedula(String ced) {cedula=ced;} setNombre(String nom) {nombre=nom;} setTitulo(String tit) {titulo=tit;} setArea(String are) {area=are;} setTelefono(String tel) {telefono=tel;} } El ensamblador del DTO Adicionalmente es necesario contar con una clase que realice el ensamblaje del DTO a partir de la entidad de profesor. Aquí se utiliza la clase ProfesorAssembler.java residente en el mismo directorio /tutorial5/src/util/. package util; import domain.Profesor; public class ProfesorAssembler { public static ProfesorDTO CreateDTO(Profesor prof) { ProfesorDTO dto = new ProfesorDTO(); dto.setId(prof.getId()); dto.setCedula(prof.getCedula()); dto.setNombre(prof.getNombre()); dto.setTitulo(prof.getTitulo()); dto.setArea(prof.getArea()); dto.setTelefono(prof.getTelefono()); return dto; } public static void Update(Profesor prof, ProfesorDTO dto) { try { prof.setId(dto.getId()); prof.setCedula(dto.getCedula()); prof.setNombre(dto.getNombre()); prof.setTitulo(dto.getTitulo()); prof.setArea(dto.getArea()); prof.setTelefono(dto.getTelefono()); } catch (Exception e) { } 1.5. Mapeo objeto/relacional 79 Desarrollo con Java, Publicación } } Compilando la capa de presentación Para compilar la capa de presentación es necesario contar con las librerías del framework Spring 3 (como se indicó antes) y con la librería servlet-api.jar ubicadas en el directorio /tutorial5/root/WEBINF/lib/. Específicamente las librerías necesarias son las siguientes: servlet-api.jar spring-asm-3.2.0.M1.jar spring-beans-3.2.0.M1.jar spring-context-3.2.0.M1.jar spring-core-3.2.0.M1.jar spring-expression-3.2.0.M1.jar spring-jdbc-3.2.0.M1.jar spring-orm-3.2.0.M1.jar spring-tx.3.2.0.M1.jar spring-web-3.2.0.M1.jar La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDisplayLayer.bat residente en el directorio /tutorial5/ (todo en una sola línea): javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/display/PageController.java src/display/ActualizarProfesor.java src/display/DetalleProfesor src/display/ListaProfesores.java src/util/ProfesorAssembler.jav src/util/ProfesorDTO.java 80 Capítulo 1. Contenidos Desarrollo con Java, Publicación 1.5.3 Capa de acceso a datos Para la capa de acceso a datos se utilizará el patrón de mapeador de datos. Para ello se requiere las librerías Spring e Hibernate. En forma conjunta estas dos librerías facilitan el desarrollo de este tipo de aplicaciones. Un DAO (Data Access Object) debe incluir todo el código para realizar la conexión con la base de datos y ejecutar las instrucciones SQL de consulta y/o actualización de datos. Generalmente escribir todo este código desde el principio resulta muy tedioso. Es por eso que Spring provee la clase HibernateDaoSupport que facilita en gran medida la escritura de clases tipo DAO. El DAO de profesor Para empezar se definirá la clase ProfesorDAO.java que residirá en el directorio /src/data. Esta clase define el conjunto de operaciones que se llevan a cabo sobre la base de datos y mediante Hibernate. El contenido de dicho archivo sería el siguiente: package data; import java.util.Collection; import util.ProfesorDTO; import util.ProfesorAssembler; import org.springframework.orm.hibernate3.support.HibernateDaoSup public class ProfesorDAO extends HibernateDaoSupport { public boolean insert(ProfesorDTO profDTO) { getHibernateTemplate().saveOrUpdate(profDTO); return true; } public boolean delete(ProfesorDTO profDTO) { getHibernateTemplate().delete(profDTO); return true; } public ProfesorDTO findById(int id) { ProfesorDTO prof; prof = (ProfesorDTO)getHibernateTemplate().get( 1.5. Mapeo objeto/relacional 81 Desarrollo con Java, Publicación ProfesorDTO.class,new Integer(id)); return prof; } public boolean update(ProfesorDTO profDTO) { getHibernateTemplate().saveOrUpdate(profDTO); return true; } public Collection findAll() { return getHibernateTemplate().find("from ProfesorDTO"); } } Como se puede observar aquí también se utiliza el DTO del profesor pero en esta ocasión es para pasar los datos desde objetos del dominio a la capa de datos. Repositorio basado en DAO Ahora es necesario asociar esta clase tipo DAO con el modelo del dominio de la capa de presentación. Este trabajo lo lleva a cabo la clase ProfesorRepositoryDAOImpl.java, que es una clase equivalente a la utiliza en el modelo del dominio. Esta clase también reside en el directorio /src/data y su contenido sería: package data; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ArrayList; import domain.ProfesorRepository; import util.ProfesorDTO; import util.ProfesorAssembler; import domain.Profesor; public class ProfesorRepositoryDAOImpl implements ProfesorRepository { private ProfesorDAO profDAO; ProfesorRepositoryDAOImpl(ProfesorDAO profDAO) { this.profDAO = profDAO; 82 Capítulo 1. Contenidos Desarrollo con Java, Publicación } public boolean insertProfesor(Profesor prof) { ProfesorDTO profDTO = ProfesorAssembler.CreateDTO(prof); return (profDAO.insert(profDTO)); } public boolean deleteProfesor(Profesor prof) { ProfesorDTO profDTO = ProfesorAssembler.CreateDTO(prof); return (profDAO.delete(profDTO)); } public Profesor findProfesor(int id) { ProfesorDTO profDTO = profDAO.findById(id); if (profDTO!=null) { Profesor prof = new Profesor(); System.out.println(profDTO.getNombre()); ProfesorAssembler.Update(prof,profDTO); return prof; } return null; } public boolean updateProfesor(Profesor prof) { ProfesorDTO profDTO = ProfesorAssembler.CreateDTO(prof); return (profDAO.update(profDTO)); } public Collection findAllProfesor() { Collection profsDTO = profDAO.findAll(); List profList = new ArrayList(); Iterator itr = profsDTO.iterator(); while (itr.hasNext()) { Profesor prof = new Profesor(); ProfesorDTO profDTO = (ProfesorDTO)itr.next(); ProfesorAssembler.Update(prof,profDTO); profList.add(prof); } return profList; } } 1.5. Mapeo objeto/relacional 83 Desarrollo con Java, Publicación Base de datos Se utilizará la misma base de datos SQLite del tutorial anterior para administrar los datos. Dicha base de datos debe llevar por nombre universidad.sqlite y debe estar ubicada en el directorio /tutorial5/root/database/. El código SQL utilizado para generar la tabla de profesores sería el siguiente: CREATE TABLE profesor (id INTEGER PRIMARY KEY, cedula VARCHAR, nombre VARCHAR, titulo VARCHAR, area VARCHAR, telefono VARCHAR INSERT INTO profesor VALUES(1,'101110111','Carlos Perez', 'Licenciado','Administracion','3456-7890'); INSERT INTO profesor VALUES(2,'202220222','Luis Torres', 'Master','Economia','6677-3456'); INSERT INTO profesor VALUES(3,'303330333','Juan Castro', 'Licenciado','Matematica','6755-7788'); Para administrar una base de datos SQLite se puede utilizar alguno de los excelentes productos creados para ello, tal como SQLiteman ó el plugin para Firefox llamado SQLite Manager Compilando la capa de datos La compilación de la capa de datos también requiere las librerías de Spring 3. La siguiente instrucción para ejecutar la compilación puede estar definida en un archivo compileDataLayer.bat residente en el directorio /tutorial5 (todo en una sola línea): javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/data/ProfesorRepositoryDAOImpl.java src/data/ProfesorDAO.java Nota: La versión del JDK debe ser superior a 6.0 84 Capítulo 1. Contenidos Desarrollo con Java, Publicación Dialecto SQLite Para que Hibernate reconozca cualquier base de datos es necesario contar con un archivo de dialecto que le identifique a Hibernate algunas características importantes del motor de bases de datos. Extrañamente, la versión actual de Hibernate no cuenta con dicho archivo de dialecto para SQLite. Sin embargo, resulta sencillo escribir dicho archivo directamente. A continuación se presenta el archivo SQLDialect.java, que reside en el directorio tutorial5/src/dialect, y cuyo contenido es: package dialect; import java.sql.Types; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.type.StandardBasicTypes; public class SQLiteDialect extends Dialect { public SQLiteDialect() { super(); registerColumnType(Types.BIT, "integer"); registerColumnType(Types.TINYINT, "tinyint"); registerColumnType(Types.SMALLINT, "smallint"); registerColumnType(Types.INTEGER, "integer"); registerColumnType(Types.BIGINT, "bigint"); registerColumnType(Types.FLOAT, "float"); registerColumnType(Types.REAL, "real"); registerColumnType(Types.DOUBLE, "double"); registerColumnType(Types.NUMERIC, "numeric"); registerColumnType(Types.DECIMAL, "decimal"); registerColumnType(Types.CHAR, "char"); registerColumnType(Types.VARCHAR, "varchar"); registerColumnType(Types.LONGVARCHAR, "longvarchar"); registerColumnType(Types.DATE, "date"); registerColumnType(Types.TIME, "time"); registerColumnType(Types.TIMESTAMP, "timestamp"); registerColumnType(Types.BINARY, "blob"); 1.5. Mapeo objeto/relacional 85 Desarrollo con Java, Publicación registerColumnType(Types.VARBINARY, "blob"); registerColumnType(Types.LONGVARBINARY, "blob"); // registerColumnType(Types.NULL, "null"); registerColumnType(Types.BLOB, "blob"); registerColumnType(Types.CLOB, "clob"); registerColumnType(Types.BOOLEAN, "integer"); registerFunction("concat", new VarArgsSQLFunction(StandardBasicTypes.STRING, "", "||", "")); registerFunction("mod", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "?1 % ?2")); registerFunction("substr", new StandardSQLFunction("substr", StandardBasicTypes.STRING)); registerFunction("substring", new StandardSQLFunction("substr", StandardBasicTypes.STRING)); } public boolean supportsIdentityColumns() {return true;} public boolean hasDataTypeInIdentityColumn() {return false;} public String getIdentityColumnString() {return "integer";} public String getIdentitySelectString() { return "select last_insert_rowid()"; } public boolean supportsLimit() {return true;} public String getLimitString(String query, boolean hasOffset) return new StringBuffer(query.length() + 20).append(query hasOffset ? " limit ? offset ?" : " limit ?").toS } public boolean supportsTemporaryTables() {return true;} public String getCreateTemporaryTableString() { return "create temporary table if not exists"; } public boolean dropTemporaryTableAfterUse() {return false;} public boolean supportsCurrentTimestampSelection() {return tr public boolean isCurrentTimestampSelectStringCallable() {retu public String getCurrentTimestampSelectString() { return "select current_timestamp"; 86 Capítulo 1. Contenidos Desarrollo con Java, Publicación } public boolean supportsUnionAll() {return true;} public boolean hasAlterTable() {return false;} public boolean dropConstraints() {return false;} public String getAddColumnString() { return "add column"; } public String getForUpdateString() {return "";} public boolean supportsOuterJoinForUpdate() {return false;} public String getDropForeignKeyString() { throw new UnsupportedOperationException( "No drop foreign key syntax supported by SQLiteDi } public String getAddForeignKeyConstraintString(String constra String[] foreignKey, String referencedTable, String[] boolean referencesPrimaryKey) { throw new UnsupportedOperationException( "No add foreign key syntax supported by SQLiteDia } public String getAddPrimaryKeyConstraintString(String constra throw new UnsupportedOperationException( "No add primary key syntax supported by SQLiteDia } public boolean supportsIfExistsBeforeTableName() {return true public boolean supportsCascadeDelete() {return false;} } Compilando el dialecto Para compilar el dialecto de SQLite se puede ejecutar el archivo compileDialect.bat desde el directorio tutorial5, y con el siguiente contenido: 1.5. Mapeo objeto/relacional 87 Desarrollo con Java, Publicación javac -cp "root/WEB-INF/classes";"root/WEB-INF/lib/*" -d root/WEB-INF/classes src/dialect/SQLiteDialect.java 1.5.4 Configuración de la aplicación Este ejemplo se puede ejecutar bajo cualquier contenedor de Servlet. Por ejemplo, para realizar la ejecución de pruebas se puede utilizar un producto como el Winstone Servlet Container que permite ejecutar servlets de forma muy sencilla. Se debe descargar el programa winstone-0.9.10.jar y copiarlo en el directorio /tutorial5/. Sin embargo (como antes), para lograr que Winstone ejecute plantillas JSP es necesario descargar algunas librerías adicionales que deben ser copiadas en el directorio /tutorial5/lib: el-api-6.0.18.jar jasper-6.0.18.jar jasper-el-6.0.18.jar jasper-jdt-6.0.18.jar jsp-api-6.0.18.jar jstl-api-1.2.jar jstl-impl-1.2.jar jtds-1.2.4.jar juli-6.0.18.jar servlet-api-2.5.jar servlet-api.jar Adicionalmente es necesario que en el directorio tutorial5/root/WEB-INF/lib se encuentren las librerías que conforman el paquete Hibernate y todas sus dependencias que básicamente son: antlr.jar 88 Capítulo 1. Contenidos Desarrollo con Java, Publicación asm-attrs.jar asm.jar c3p0-0.9.0.jar cglib-2.1.3.jar commons-collections-3.1.jar commons-dbcp-1.4.jar commons-logging-1.1.1.jar commons-pool-1.6.jar dom4j-1.6.1.jar hibernate-jpa-2.0-api-1.0.1.Final.jar hibernate3.jar javassist-3.12.0.GA.jar jta-1.1.jar slf4j-api-1.6.1.jar sqlite-jdbc-3.6.0.jar Archivo de contexto Es necesario crear un archivo de contexto en donde se realizará la creación de una serie de objetos necesarios. Este archivo lleva por nombre context.xml y residirá en el directorio tutorial5/root/WEBINF y su contenido sería: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0. http://www.springframework.org/schema/context 1.5. Mapeo objeto/relacional 89 Desarrollo con Java, Publicación http://www.springframework.org/schema/context/spring-context<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataS destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClas <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFacto <property name="dataSource" ref="dataSource" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/> <property name="configLocation"> <value>classpath:hibernate.cfg.xml</value> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransact <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="profesorDAO" class="data.ProfesorDAO"> <property name="sessionFactory"><ref local="sessionFactory"/> </bean> <bean id="profesorRepository" class="data.ProfesorRepositoryDAO <constructor-arg> <ref bean="profesorDAO"/> </constructor-arg> </bean> <context:property-placeholder location="WEB-INF/jdbc.properties </beans> También es necesario el archivo jdbc.properties que reside en el mismo directorio tutorial5/root/WEB-INF y cuyo contenido es: jdbc.driverClassName=org.sqlite.JDBC jdbc.url=jdbc:sqlite:root/database/universidad.sqlite jdbc.username=sa jdbc.password=root 90 Capítulo 1. Contenidos Desarrollo con Java, Publicación Archivo de configuración de Hibernate Hibernate 3 requiere de su propio archivo de configuración el cual se llamará hibernate.cfg.xml y residirá en el directorio /tutorial5/root/WEB-INF/classes y su contenido es: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0 <hibernate-configuration> <session-factory> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="dialect">dialect.SQLiteDialect</property> <mapping resource="ProfesorDTO.hbm.xml"/> </session-factory> </hibernate-configuration> Metadatos para la clase Profesor Hibernate 3 utiliza archivos de metadatos para establecer la relación entre los campos de cada tabla y los atributos de las clases. En este caso se utilizará un archivo llamado ProfesorDTO.hbm.xml que residirá en el mismo directorio tutorial5/root/WEB-INF/classes y cuyo contenido sería: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mappin "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="util.ProfesorDTO" table="profesor"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="cedula" column="cedula" type="string"></prope <property name="nombre" column="nombre" type="string"></prope <property name="titulo" column="titulo" type="string"></prope <property name="area" column="area" type="string"></property> 1.5. Mapeo objeto/relacional 91 Desarrollo con Java, Publicación <property name="telefono" column="telefono" type="string"></p </class> </hibernate-mapping> El archivo de configuración de servlets Por último, es necesario crear el archivo de definición de servlets para esta aplicación. Como es costumbre su nombre será web.xml, su ubicación será tutorial5/root/WEB-INF: <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_ version="2.4"> <display-name>Sistema Universitario</display-name> <description>Ejemplo de Mapeo Relacional/Objeto/description> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/context.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>ActualizarProfesor</servlet-name> <servlet-class>display.ActualizarProfesor</servlet-class> </servlet> <servlet> <servlet-name>DetalleProfesor</servlet-name> <servlet-class>display.DetalleProfesor</servlet-class> </servlet> <servlet> <servlet-name>ListaProfesores</servlet-name> <servlet-class>display.ListaProfesores</servlet-class> </servlet> 92 Capítulo 1. Contenidos Desarrollo con Java, Publicación <servlet-mapping> <servlet-name>ActualizarProfesor</servlet-name> <url-pattern>/actualizarProfesor</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>DetalleProfesor</servlet-name> <url-pattern>/detalleProfesor</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ListaProfesores</servlet-name> <url-pattern>/listaProfesores</url-pattern> </servlet-mapping> </web-app> Ejecución del Tutorial Para ejecutar el servidor de servlets se puede crear un archivo de instrucciones, llamado run.bat (todo en una sola línea), similar al siguiente en el directorio /tutorial5/: java -jar winstone-0.9.10.jar --httpPort=8089 --commonLibFolder=lib --useJasper=true --webroot=root Luego se puede acceder a la aplicación desde cualquier visualizador web y apuntando a la dirección http://localhost:8089/listaProfesores 1.6 Uso de JSTL La tecnología JavaServer Pages Standard Tag Library (JSTL) es un componente de Java EE. Extiende las ya conocidas JavaServer Pages (JSP) proporcionando cuatro bibliotecas de etiquetas (Tag Libraries) con utilidades ampliamente utilizadas en el desarrollo de páginas web dinámicas. Estas bibliotecas de etiquetas extienden de la especificación de JSP (la cual a su vez extiende de la especificación de Servlet). Su API permite además desarrollar bibliotecas propias de etiquetas. 1.6. Uso de JSTL 93 Desarrollo con Java, Publicación Las bibliotecas englobadas en JSTL son: core: iteraciones, condicionales, manipulación de URL y otras funciones generales. xml: para la manipulación de XML y para XMLTransformation. sql: para gestionar conexiones a bases de datos. fmt: para la internacionalización y formateo de las cadenas de caracteres como cifras. 1.6.1 Creación de la base de datos Para este ejemplo se utilizará nuevamente la base de datos de prueba, llamada universidad.db, utilizando SQLite. Es posible utilizar diferentes herramientas para crear bases de datos en este formato, entre ellas se encuentra el plugin para Firefox llamado SQLite Manager, o bien, la herramienta SQLiteBrowser. Por el momento solo se utilizará una tabla con información de profesores de una universidad. El código SQL para crear dicha tabla sería el siguiente: CREATE TABLE "profesor" ( id INTEGER PRIMARY KEY ASC, cedula VARCHAR, nombre VARCHAR, titulo VARCHAR, area VARCHAR, telefono VARCHAR ); Luego de crear la base de datos se agregaron algunos datos de ejemplo a esta tabla. Las siguientes instrucciones SQL permiten poblar la tabla alguna información: INSERT INTO profesor (id,cedula,nombre,titulo,area,telefono) VALUES (1,'101110111','Carlos Perez Rojas','Licenciado', 'Administracion','3456-7890'); 94 Capítulo 1. Contenidos Desarrollo con Java, Publicación INSERT INTO profesor (id,cedula,nombre,titulo,area,telefono) VALUES (2,'202220222','Luis Torres','Master', 'Economia','6677-3456'); INSERT INTO profesor (id,cedula,nombre,titulo,area,telefono) VALUES (3,'303330333','Juan Castro','Licenciado', 'Matematica','67455-7788'); 1.6.2 Página de listado de profesores La primer página consistirá del listado de todos los profesores que se encuentran en la base de datos. El código que se muestra a continuación (llamado listaProfesores.jsp) establece la conexión con la base de datos (utilizado JDBC), ejecuta la instrucción de consulta, recupera los datos y crea una tabla HTML con la información obtenida. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %> <sql:setDataSource dataSource="universidad"></sql:setDataSource> <sql:query var="profesores"> select * from profesor </sql:query> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Listado de profesores</h2> <table> <thead> <tr><th>Cedula</th><th>Nombre</th> <th>Titulo</th><th>Acciones</th></tr> </thead> <tbody> <c:forEach var="profesor" begin="0" items="${profesores.rows} <tr><td>${profesor.cedula}</td> <td>${profesor.nombre}</td> <td>${profesor.titulo}</td> 1.6. Uso de JSTL 95 Desarrollo con Java, Publicación <td><a href='/detalleProfesor.jsp?id=${profesor.id}'> <input type="submit" value="Detalle"/></a> <a href='/eliminarProfesor.jsp?id=${profesor.id}'> <input type="submit" value="Eliminar"/></a></td></t </c:forEach> </tbody> <tfoot> <tr><td><a href='/agregarProfesor.jsp'> <input type="submit" name="action" value="Agregar"/></a> </td><td></td><td></td><td></td></tr> </tfoot> </table> </html> Es importante observar en este código que existen enlaces que accederán a otras páginas para realizar acciones con los datos. Por ejemplo, el enlace de “Detalle” ejecutará la página detalleProfesor.jsp con el parámetro ID; y el enlace de “Eliminar” ejecutará la página eliminarProfesor.jsp con el mismo parámetro ID. También está presente otro enlace “Agregar” que invocará a la página agregarProfesor.jsp pero sin parámetros. 1.6.3 Detalle del profesor La página detalleProfesor.jsp recibe como parámetro el ID de un profesor, recupera sus datos desde la base y datos, y los muestra en un formulario HTML. La estructura general de la consulta a la base de datos es muy similar a la anterior con la diferencia que se recupera solo un registro particular. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %> <sql:setDataSource dataSource="universidad"></sql:setDataSource> <sql:query var="profesores"> select * from profesor where id = ? <sql:param value="${param.id}" /> </sql:query> 96 Capítulo 1. Contenidos Desarrollo con Java, Publicación <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="style.css"> </head> <h1>Sistema Universitario</h1> <h2>Detalle de Profesor</h2> <form name="ActualizarProfesor" action="actualizarProfesor" met <table style="width:400px;"> <thead> <tr><th></th><th></th></tr> </thead> <tbody> <c:forEach var="profesor" begin="0" items="${profesores.rows <input type="hidden" name="id" value="${profesor.id}"/> <tr><td>Nombre:</td><td> <input type="text" name="nombre" value="${profesor.nombre}" <tr><td>Cedula:</td><td> <input type="text" name="cedula" value="${profesor.cedula}" <tr><td>Titulo:</td><td> <input type="text" name="titulo" value="${profesor.titulo}" <tr><td>Area:</td><td> <input type="text" name="area" value="${profesor.area}"/></ <tr><td>Telefono:</td><td> <input type="text" name="telefono" value="${profesor.telefo </tbody> <tfoot> <tr><td><input type="submit" value="Actualizar" /></td><td></ </c:forEach> </tfoot> </tbody> </table> </form> </html> Este código también cuenta con un enlace adicional “Actualizar” que permite tomar la información del formulario y realizar la actualización de datos en la base de datos, mediante la página actualizarProfesor.jsp 1.6. Uso de JSTL 97 Desarrollo con Java, Publicación 1.6.4 Hoja de estilo Con el fin de mejorar la apariencia de este ejemplo se ha desarrollado una pequeña hoja de estilo (css) que permite organizar mejor los datos de la tabla y el formulario. El archivo llamado style.css cuenta con el siguiente código: body { line-height: 1.6em; font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; color: #009; } table { font-size: 12px; margin: 45px; width: 480px; text-align: left; border-collapse: collapse; } th, tfoot td { font-size: 13px; font-weight: normal; padding: 8px; background: #b9c9fe; border-top: 4px solid #aabcfe; border-bottom: 1px solid #fff; color: #039; text-align: center; } td { padding: 8px; background: #e8edff; border-bottom: 1px solid #fff; color: #669; border-top: 1px solid transparent; } tr:hover td { background: #d0dafd; color: #339; } 98 Capítulo 1. Contenidos Desarrollo con Java, Publicación input[type="text"] { width: 250px; } 1.6.5 Ambiente de ejecución Para ejecutar este ejemplo es necesario contar con un servidor de servlets que permita también la ejecución de plantillas JSTL. La herramienta más utilizada para esto es el Apache Tomcat, el cuál es muy potente y cuenta con gran cantidad de parámetros de configuración. Sin embargo, para propósito de desarrollo y depuración de programas basta con un ambiente más liviano tal como Winstone. Winstone consiste de un único archivo de menos de 350 KB, llamado winstone-0.9.10.jar, el cual puede ser ejecutado directamente mediante Java. Sin embargo, poder utilizar plantillas JSP se requiere de la herramienta Jasper que consiste de múltiples librerías adicionales. Para acceder a la base de datos SQLite, mediante JDBC, es necesario contar con una librería que incluya el driver adecuado. Aún cuando existen diferentes librerías que hacen esto, ninguna es pequeña. Estructura de directorios La ubicación de los diferentes archivos de código, y librerías se muestra en el siguiente esquema de directorios: tutorial6 run.bat winstone-0.9.10.jar database universidad.db root style.css listaProfesores.jsp detalleProfesor.jsp lib 1.6. Uso de JSTL 99 Desarrollo con Java, Publicación el-api-6.0.18.jar jasper-6.0.18.jar jasper-el-6.0.18.jar jsp-api-6.0.18.jar jstl-impl-1.2jar jtds-1.2.4.jar juli-6.0.18.jar servlet-api-2.5.jar sqlite-jdbc-3.5.9.jar 1.6.6 Ejecución del ejemplo Teniendo instalado el JDK de Java (no el JRE) basta con ejecutar el archivo run.bat para iniciar el servidor. El archivo run.bat cuenta con las siguientes instrucciones (todo en una sola línea) : java -jar winstone-0.9.10.jar --webroot=root --httpPort=8089 --commonLibFolder=lib --useJasper=true --useJNDI=true --jndi.resource.universidad=javax.sql.DataSource --jndi.param.universidad.driverClassName=org.sqlite.JDBC --jndi.param.universidad.url=jdbc:sqlite:database/universida Luego se debe apuntar el visualizador (browser) de Web a la dirección http://localhost:8089/listaProfesores.jsp Nota: Es posible que el JDK de Java se encuentre instalado en otro directorio en su máquina. 1.7 Modelo/Vista/Controlador Spring provee muchas opciones para configurar una aplicación. La más popular es usar archivos XML. 100 Capítulo 1. Contenidos Desarrollo con Java, Publicación 1.7.1 Capa del dominio Se utilizará una única clase para ilustrar el desarrollo de una aplicación MVC. En este caso será la clase de Profesor.java. Esta clase se ubica en el directorio src/domain y su código es el siguiente: package universidad.domain; import java.io.Serializable; public class Profesor implements Serializable { private String nombProf; private String idProf; private String tituloProf; public String getNombProf() {return nombProf;} public void setNombProf(String n) {nombProf=n;} public String getIdProf() {return idProf;} public void setIdProf(String id) {idProf=id;} public String getTituloProf() {return tituloProf;} public void setTituloProf(String tit) {tituloProf = tit;} } 1.7.2 Capa de servicio Aquí se utilizará una clase sencilla, llamada SimpleProfesorManager.java, para configurar la capa de servicio. Adicionalmente se utilizará una interfaz, llamada ProfesorManager.java, en donde se definirán los métodos utilizados por dicha clase. El código de dicha interfaz se localiza en el directorio src/service y es el siguiente: package universidad.service; import java.io.Serializable; import java.util.List; import universidad.domain.Profesor; public interface ProfesorManager extends Serializable{ public List<Profesor> getProfesores(); } El código de la clase SimpleProfesorManager.java se muestra a continuación y se ubica en el mismo directorio: 1.7. Modelo/Vista/Controlador 101 Desarrollo con Java, Publicación package universidad.service; import java.util.ArrayList; import java.util.List; import universidad.domain.Profesor; public class SimpleProfesorManager implements ProfesorManager { private List<Profesor> profesores; public List<Profesor> getProfesores() { return profesores; } public void setProfesores(List<Profesor> profesores) { this.profesores = profesores; } } 1.7.3 El controlador La capa de presentación consta de dos componentes, el controlador y la vista. El controlador consta de una sola clase, llamada ProfesorController.java, que carga el modelo de datos e invoca a la vista, llamada profesorView.jsp. El código se muestra a continuación y se ubica en el directorio src/web.: package universidad.web; import org.springframework.web.servlet.mvc.Controller; import org.springframework.web.servlet.ModelAndView; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Map; import java.util.HashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import universidad.service.ProfesorManager; public class ProfesorController implements Controller { protected final Log logger = LogFactory.getLog(getClass()); private ProfesorManager ProfesorManager; 102 Capítulo 1. Contenidos Desarrollo con Java, Publicación public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String now = (new java.util.Date()).toString(); logger.info("returning profesor view with " + now); Map<String, Object> myModel = new HashMap<String, Object>(); myModel.put("now", now); myModel.put("profesores", this.ProfesorManager.getProfesores() return new ModelAndView("profesorView", "model", myModel); } public void setProfesorManager(ProfesorManager ProfesorManager) this.ProfesorManager = ProfesorManager; } } 1.7.4 La vista El archivo que genera la vista se llama profesorView.jsp y consta de varias etiquetas que permiten listar la información de profesores. El código es el siguiente y este archivo se ubica en el directorio war/WEB-INF/jsp.: <%@ include file="/WEB-INF/jsp/include.jsp" %> <html> <head><title><fmt:message key="title"/></title></head> <body> <h1><fmt:message key="heading"/></h1> <p><fmt:message key="mensaje"/> <c:out value="${model.now}"/>< <h3>Profesores</h3> <table border="1"> <tr><th>Nombre</th><th>Cedula</th><th>Titulo</th></tr> <c:forEach items="${model.profesores}" var="prof"> <tr><td><c:out value="${prof.nombProf}"/></td> <td><c:out value="${prof.idProf}"/></td> 1.7. Modelo/Vista/Controlador 103 Desarrollo con Java, Publicación <td><c:out value="${prof.tituloProf}"/></td></tr> </c:forEach> </table> </body> </html> Como se puede observar es necesario contar con el archivo include.jsp que se ubica en el directorio war/WEB-INF/jsp y cuyo código es simplemente el siguiente: <%@ page session="false"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> También existe un archivo que cuenta con algunos mensajes que se despliegan en la página y se llama messages.properties. Este archivo se ubica en el directorio war/WEB-INF/classes y su contenido es el siguiente: title=Sistema Universitario heading=Sistema Universitario - Inicio mensaje=Bienvenido, la fecha actual es 1.7.5 Configuración Para configurar la aplicación es necesario contar con un archivo web.xml ubicado en el directorio war/WEB-INF con el siguiente contenido.: <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" > <servlet> <servlet-name>universidad</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServl </servlet-class> 104 Capítulo 1. Contenidos Desarrollo con Java, Publicación <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>universidad</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file> index.jsp </welcome-file> </welcome-file-list> </web-app> También se utilizará otro archivo de configuración para crear objetos de ejemplo que permitan probar la aplicación. Esto se hará utilizando el archivo universidad-servlet.xml que se ubica en el mismo directorio war/WEB-INF. Su contenido es el siguiente: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd <bean id="profesorManager" class="universidad.service.SimpleProfesorManager"> <property name="profesores"> <list> <ref bean="profesor1"/> <ref bean="profesor2"/> <ref bean="profesor3"/> </list> </property> </bean> <bean id="profesor1" class="universidad.domain.Profesor"> <property name="nombProf" value="Juan Jimenez"/> <property name="idProf" value="303450678"/> <property name="tituloProf" value="Licenciado"/> </bean> <bean id="profesor2" class="universidad.domain.Profesor"> <property name="nombProf" value="Pedro Perez"/> 1.7. Modelo/Vista/Controlador 105 Desarrollo con Java, Publicación <property name="idProf" value="102340567"/> <property name="tituloProf" value="Maestria"/> </bean> <bean id="profesor3" class="universidad.domain.Profesor"> <property name="nombProf" value="Luisa Linares"/> <property name="idProf" value="407860887"/> <property name="tituloProf" value="Licenciada"/> </bean> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessa <property name="basename" value="messages"/> </bean> <bean name="/hello.htm" class="universidad.web.ProfesorControlle <property name="profesorManager" ref="profesorManager"/> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceVi <property name="viewClass" value="org.springframework.web.servlet.view.JstlView <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans> Por último es necesario contar con un archivo inicial que reenvíe las solicitudes que se realizan al sitio, este archivo se llama index.jsp y se ubica en el directorio war. Su contenido es simplemente: <%@ include file="/WEB-INF/jsp/include.jsp" %> <c:redirect url="/hello.htm"/> 1.7.6 Compilación y ejecución Para compilar la aplicación es necesario copiar en el directorio war/WEB-INF/lib las librerías spring.jar y spring-webmvc.jar del framework Spring. También se requieren las librerías servlet-api2.5.jar y commons-logging-1.1.1.jar pero estas pueden estar ubicados simplemente en el directorio lib. 106 Capítulo 1. Contenidos Desarrollo con Java, Publicación Un script que permite compilar la aplicación tendría la siguiente forma (todo en una sola línea): javac -classpath lib/servlet-api-2.5.jar;lib/commons-logging-1.1. war/WEB-INF/lib/spring-webmvc.jar -d war/WEB-INF/classes src/service/*.java src/domain/*.java src/web/*.java Utilizando winstone se puede correr la aplicación utilizando simplemente: java -jar winstone-0.9.10.jar --webroot=war --useJasper luego basta con apuntar el navegador a la dirección: http://localhost:8080 1.8 Controlador Spring de aplicación con Spring provee muchas opciones para configurar una aplicación. La más popular es usar archivos XML. 1.8.1 Capa del dominio Se utilizará una única clase para ilustrar el desarrollo de una aplicación MVC. En este caso será la clase de Profesor.java. Esta clase se ubica en el directorio src/domain y su código es el siguiente: package universidad.domain; import java.io.Serializable; public class Profesor implements Serializable { private String nombProf; private String idProf; private String tituloProf; public String getNombProf() {return nombProf;} public void setNombProf(String n) {nombProf=n;} public String getIdProf() {return idProf;} 1.8. Controlador de aplicación con Spring 107 Desarrollo con Java, Publicación public void setIdProf(String id) {idProf=id;} public String getTituloProf() {return tituloProf;} public void setTituloProf(String tit) {tituloProf = tit;} } 1.8.2 Capa de servicio Aquí se utilizará una clase sencilla, llamada SimpleProfesorManager.java, para configurar la capa de servicio. Adicionalmente se utilizará una interfaz, llamada ProfesorManager.java, en donde se definirán los métodos utilizados por dicha clase. El código de dicha interfaz se localiza en el directorio src/service y es el siguiente: package service; import java.io.Serializable; import java.util.List; import domain.Profesor; public interface ProfesorManager extends Serializable{ public List<Profesor> getProfesores(); public Profesor getById(String id); } El código de la clase SimpleProfesorManager.java se muestra a continuación y se ubica en el mismo directorio: package service; import java.util.ArrayList; import java.util.List; import java.util.Iterator; import domain.Profesor; public class SimpleProfesorManager implements ProfesorManager { private List<Profesor> profesores; public List<Profesor> getProfesores() { return profesores; } public void setProfesores(List<Profesor> profesores) { this.profesores = profesores; } public Profesor getById(String id) { 108 Capítulo 1. Contenidos Desarrollo con Java, Publicación Iterator itr = profesores.iterator(); Profesor prof; while (itr.hasNext()) { prof = (Profesor)itr.next(); if (prof.getIdProf().equals(id)) return prof; } return null; } } 1.8.3 El controlador La capa de presentación consta de dos componentes, el controlador y la vista. El controlador consta de una sola clase, llamada ProfesorController.java, que carga el modelo de datos e invoca a las vistas, llamada listaProfesores.jsp y detalleProfesor.jsp. El código se muestra a continuación y se ubica en el directorio src/display.: package display; import import import import import import import org.springframework.beans.factory.annotation.Autowired; org.springframework.stereotype.Controller; org.springframework.web.bind.annotation.RequestMapping; org.springframework.web.bind.annotation.RequestMethod; org.springframework.web.bind.annotation.PathVariable; org.springframework.web.bind.annotation.RequestParam; org.springframework.web.servlet.ModelAndView; import java.util.Map; import java.util.HashMap; import service.ProfesorManager; import domain.Profesor; @Controller @RequestMapping("/profesor") public class ProfesorController { @Autowired 1.8. Controlador de aplicación con Spring 109 Desarrollo con Java, Publicación private ProfesorManager ProfesorManager; @RequestMapping(value="/listado", method = RequestMethod.GET) public ModelAndView listado() { Map<String, Object> myModel = new HashMap<String, Object>(); myModel.put("profesores", this.ProfesorManager.getProfesores() return new ModelAndView("listaProfesores", "model", myModel); } @RequestMapping(value="/detalleProfesor/{idProf}", method = Requ public ModelAndView detalleProfesor(@PathVariable("idProf") Stri Profesor prof = this.ProfesorManager.getById(id); if (prof == null) System.out.println("NULO"); return new ModelAndView("detalleProfesor", "profesor", prof); } @RequestMapping(value="/actualizarProfesor/{idProf}", params = { public String actualizarProfesor( @PathVariable("idProf") String id, @RequestParam("nombre") String nombre, @RequestParam("titulo") String titulo, @RequestParam("cedula") String cedula ) { Profesor prof = this.ProfesorManager.getById(id); prof.setNombProf(nombre); prof.setTituloProf(titulo); prof.setIdProf(cedula); return "forward:/profesor/listado"; } public void setProfesorManager(ProfesorManager ProfesorManager) this.ProfesorManager = ProfesorManager; } } 110 Capítulo 1. Contenidos Desarrollo con Java, Publicación 1.8.4 La vistas El archivo que genera la vista de profesores se llama listaProfesores.jsp y consta de varias etiquetas que permiten listar la información de profesores. El código es el siguiente y este archivo se ubica en el directorio /root/pages.: <%@ include file="/pages/include.jsp" %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="/resources/style.css"> <meta http-equiv="Content-Type" content="text/html; charset=U </head> <h1>Sistema Universitario</h1> <h2>Listado de profesores</h2> <table> <thead> <tr><th>Nombre</th><th>Cedula</th><th>Titulo</th><th>Accione </thead> <tbody> <c:forEach items="${model.profesores}" var="prof"> <tr><td>${prof.nombProf}</td> <td>${prof.idProf}</td> <td>${prof.tituloProf}</td> <td><a href='/profesor/detalleProfesor/${prof.idProf}'> <input type="submit" value="Detalle"/></a> <a href='/profesor/eliminarProfesor/${prof.idProf}'> <input type="submit" value="Eliminar"/></a></td></t </c:forEach> </tbody> <tfoot> <tr><td><a href='/profesor/agregarProfesor'> <input type="submit" name="action" value="Agregar"/></a> </td><td></td><td></td><td></td></tr> </tfoot> </table> </body> </html> Como se puede observar es necesario contar con el archivo inclu1.8. Controlador de aplicación con Spring 111 Desarrollo con Java, Publicación de.jsp que se ubica en el mismo directorio root/pages y cuyo código es simplemente el siguiente: <%@ page session="false"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> El otro archivo de vista es el que genera el detalle del profesor, es llamado detalleProfesor.jsp y se encuentra ubicado en el mismo directorio /root/pages <%@ include file="/pages/include.jsp" %> <html> <head> <title>Sistema Universitario</title> <link rel="stylesheet" href="/resources/style.css"> <meta http-equiv="Content-Type" content="text/html; charset=U </head> <h1>Sistema Universitario</h1> <h2>Detalle de Profesor</h2> <form name="ActualizarProfesor" action="/profesor/actualizarPro <table style="width:400px;"> <thead> <tr><th></th><th></th></tr> </thead> <tbody> <input type="hidden" name="id" value="${profesor.idProf}"/> <tr><td>Nombre:</td><td> <input type="text" name="nombre" value="${profesor.nombProf <tr><td>Cedula:</td><td> <input type="text" name="cedula" value="${profesor.idProf}" <tr><td>Titulo:</td><td> <input type="text" name="titulo" value="${profesor.tituloPr </td></tr> </tbody> <tfoot> <tr><td><input type="submit" value="Actualizar" /></td><td></ </tfoot> </tbody> </table> </form> 112 Capítulo 1. Contenidos Desarrollo con Java, Publicación </html> 1.8.5 Configuración Para configurar la aplicación es necesario contar con un archivo web.xml ubicado en el directorio root/WEB-INF con el siguiente contenido.: <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Sistema Universitario</display-name> <servlet> <servlet-name>universidad</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>universidad</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/universidad-servlet.xml</param-valu </context-param> <listener> <listener-class>org.springframework.web.context.ContextLo </listener> </web-app> 1.8. Controlador de aplicación con Spring 113 Desarrollo con Java, Publicación También se utilizará otro archivo de configuración para crear objetos de ejemplo que permitan probar la aplicación. Esto se hará utilizando el archivo universidad-servlet.xml que se ubica en el mismo directorio root/WEB-INF. Su contenido es el siguiente: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/bean http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.x http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <context:component-scan base-package="display" /> <bean id="profesorManager" class="service.SimpleProfesorManager"> <property name="profesores"> <list> <ref bean="profesor1"/> <ref bean="profesor2"/> <ref bean="profesor3"/> </list> </property> </bean> <bean id="profesor1" class="domain.Profesor"> <property name="nombProf" value="Juan Jimenez"/> <property name="idProf" value="303450678"/> <property name="tituloProf" value="Licenciado"/> </bean> <bean id="profesor2" class="domain.Profesor"> <property name="nombProf" value="Pedro Perez"/> <property name="idProf" value="102340567"/> <property name="tituloProf" value="Maestria"/> </bean> <bean id="profesor3" class="domain.Profesor"> <property name="nombProf" value="Luisa Linares"/> 114 Capítulo 1. Contenidos Desarrollo con Java, Publicación <property name="idProf" value="407860887"/> <property name="tituloProf" value="Licenciada"/> </bean> <bean class="org.springframework.web.servlet.view.InternalRes <property name="prefix" value="/pages/"/> <property name="suffix" value=".jsp"/> </bean> <mvc:resources mapping="/resources/**" location="/web-resourc <mvc:annotation-driven /> </beans> Por último es necesario contar con una hoja de estilo que se ubicaría en el directorio /root/web-resources y se llamaría style.css: body { line-height: 1.6em; font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif; color: #009; } table { font-size: 12px; margin: 45px; width: 480px; text-align: left; border-collapse: collapse; } th, tfoot td { font-size: 13px; font-weight: normal; padding: 8px; background: #b9c9fe; border-top: 4px solid #aabcfe; border-bottom: 1px solid #fff; color: #039; text-align: center; } td { padding: 8px; background: #e8edff; 1.8. Controlador de aplicación con Spring 115 Desarrollo con Java, Publicación border-bottom: 1px solid #fff; color: #669; border-top: 1px solid transparent; } tr:hover td { background: #d0dafd; color: #339; } input[type="text"] { width: 250px; } 1.8.6 Ejecución Utilizando winstone se puede correr la aplicación utilizando simplemente: java -jar winstone-0.9.10.jar --httpPort=8089 --commonLibFolder=l luego basta con apuntar el navegador a la dirección: http://localhost:8089/profesor/listado 116 Capítulo 1. Contenidos
© Copyright 2024