
Ejercicios

Recursos

Videos
A lo largo del curso encontrarás estos iconos, que son enlaces a los diferentes recursos del curso (ejercicios, ejemplos, ficheros descargables y videos premium).
Índice del curso de MVC
- Modelo Vista Controlador (modelo 2)
- Grid Class
- Procesar toda la operativa en doGet o doPost
- Definir constantes de aplicación en el web.xml
- Ejercicio - Lista de invitados
- Ejercicio libreria
- Ejercicio fútbol
- Trabajo con fechas
- Hospital I
- Listado libros
- Ejercicio calidades
- Sesión
- Listener de sesión
- Ejercicio Login
- Ejercicio mensajeria
- Ejercicio foro
- Contexto
- Upload file
- Web Services
Modelo 1
Se usaban Servlets, JSP y custom Tags, pero sin utilizar una estructura bien definida.
Modelo Vista Controlador (modelo 2)
- Vista: Son las pantallas que interaccionan con el usuario (la interfaz del usuario) (html y jsp). Todo lo que tenga que ver con la vista se almacena en la carpeta WEB-CONTENT.
- Controlador: Recibe las peticiones de la vista y se la manda al modelo (servlets).
- Modelo: Es la parte que se comunica con el servidor (consulta la base de datos, manda un correo, etc.)Son ficheros Java o EJB. En él, no debe haber ninguna referencia al protocolo http, ni request, ni response, ni session, etc.

Cuando un proyecto comienza a crecer...
El modelo se almacena en un proyecto java común, y la vista en un proyecto web.
Saludar con MVC
entrada.jsp<form action="ControllerServlet">
<input type="text" name="nombre">
<input type="submit">
</form>
ControllerServlet.java
protected void doPost(HttpServletRequest, HttpServletResponse) throws ServletException, IOException{
String resultado = Servicio.saludar(request.getParameter("nombre"));
request.setAttribute("resultado", resultado);
request.getRequestDispatcher("salida.jsp").forward(request, response);
}
Servicio
public class Servicio{
public static String saludar(String nombre){
return "hola" + nombre;
}
}
salida.jsp
<%= request.getAttribute("resultado") %>
Ejercicio calculadora
Hacer una calculadora siguiendo el patrón MVC. Si el resultado es mayor que 1000, el controlador redirigirá a una página diferente en la que se felicitará al usuario.
Solventando problemas de codificación
Configuraremos el cliente para recibir y enviar la información en UTF-8.
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
Definiendo la codificación de todas las peticiones
Cuando se envía la petición por POST, el servidor TOMCAT no es capaz de saber cuál es el formato de codificación del cliente. Como posible solución está crear un filtro para que todas las peticiones pasen por él y dónde se especifique que sean en formato UTF-8.
UTF8Filter.java
public class UTF8Filter implements Filter {
private String encoding;
//Recogemos el tipo de codificación definido en el web.xml
public void init( FilterConfig filterConfig ) throws ServletException {
encoding = filterConfig.getInitParameter( "requestEncoding" );
}
// Metemos en la request el formato de codificacion UTF-8
public void doFilter( ServletRequest request, ServletResponse response, FilterChain fc )throws IOException, ServletException {
request.setCharacterEncoding( encoding );
fc.doFilter( request, response );
}
public void destroy() {}
}
web.xml
…
<filter>
<filter-name>UTF8Filter</filter-name>
<filter-class>com.pablomonteserin.filter.UTF8Filter</filter-class>
<init-param>
<param-name>requestEncoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>UTF8Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Configurando el servidor para trabajar con UTF8
En Tomcat por defecto se especifica el formato de codificación ISO-8859-1. Para cambiar la codificación tenemos que modificar el archivo server.xml que se encuentra en DIRECTORIO_INSTALACION_TOMCAT\conf\server.xml.
Añadimos el atributo URIEncoding=“UTF-8” en la etiqueta <Connector port="8080" … />
<Connector port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="UTF-8"/>
Modelo 5 capas
Vista.La apariencia de la página. Son los .jsp, .html, .xhtml, etc.
→ Controlador.
Recoge la información de la vista y se la manda al BO.
→
BO.
Contiene las llamadas a cada una de las pequeñas operaciones independientes que componen la operación que queremos realizar. En el caso de estar realizando operaciones contra una base de datos, el el BO abrimos y cerramos la conexión con la base de datos. Llama al DAO.
→
DAO
En él haces operaciones contra sistemas ajenos a nuestra aplicación (una base de datos, un web service, etc.). También se le llama capa de integración.
Son las clases 'tontas'. Contienen métodos nucleares que realizan operaciones concretas.
→
Base de datos
Según las recomendaciones de la JSR (Java Specification Request) y de la JCP (Java Comunity Proccess), si los casos de uso están bien definidos habrá un DAO por cada BO y un BO por cada Action.
Recomendación: no usar métodos estáticos para llamar al BO y al DAO
Las variables definidas dentro de un método estático son compartidas por todos los hilos que llamen al método.
Por tanto:
Si tenemos un método estático consultarPaciente(int id), y dos usuarios distintos que simultáneamente llaman a este método, puede ocurrir lo siguiente:
el primer usuario consulta para la id 4 y obtiene un paciente, pero antes de que llegue al return del método, llega el segundo usuario y consulta para la id 7, modificando la variable paciente que almacena el resultado de la consulta y que es común para ambos. Por tanto, el primer usuario, obtendrá el paciente que consulta el segundo.
Ni las llamadas al BO, ni las llamadas al DAO deben ser hechas mediante métodos estáticos.
Procesar toda la operativa en doGet o doPost
Desde el método doGet podemos llamar a:
doPost(request,response);
y desde el método doPost podemos llamar a:
doGet(request,response);
Patrones de diseño → Singleton
Un patrón de diseño es una solución a un problema concreto en el desarrollo de software.
Definir constantes de aplicación en el web.xml
<context-param>
<param-name>foo</param-name>
<param-value>bar</param-value>
</context-param>
<%=getServletContext().getInitParameter("foo") %>
${initParam.foo}
Ejercicio - Lista de invitados
Hacer una página web para una lista de invitados con 4 secciones. Una para consultar los invitados, otra para dar de alta un nuevo invitado y otra para darlo de baja.La tabla que usaremos tendrá dos campos: nombre (VARCHAR) e ID (INT, AUTOINCREMENT, PK).
Cada uno de los siguientes pantallazos representa una página jsp diferente.
Ejercicio fútbol
La segunda carga una collection de beans de Equipo, y la tercera carga una collection de beans de Jugador en función de un parámetro (EQUIPO_COD) que fue procesado en el controlador.
Entidades para usar en Hibernate
Trabajo con fechas
Paso de String a Date:
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
Date fecha = s.parse(request.getParameter("fechaAlta"));
Paso de Date a String:
SimpleDateFormat s = new SimpleDateFormat("dd-MM-yyyy");
String fechaAlta = s.format(fecha); //fecha es un objeto de tipo Date
Descomposición de una fecha en formato String:
String fecha = ''24-07-1982'';
String [] fechaSplitada = fecha.split(''-'');
String dia = fechaSplitada[0];
String mes = fechaSplitada[1];
String anio = fechaSplitada[2];
Paso de Date a GregorianCalendar:
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(fecha); //fecha es un objeto de tipo Date
String dia = gc.get(GregorianCalendar.DAY_OF_MONTH);
String mes = gc.get(GregorianCalendar.MONTH)+1;
String anio = gc.get(GregorianCalendar.YEAR);
Construcción de un objeto GregorianCalendar a partir del día, mes y año por separado:
new GregorianCalendar(int year, int month, int dayOfMonth)
Para pasar de String a GregorianCalendar hay que pasar previamente de String a Date.
Etiqueta JSTL para formato de fechas
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:formatDate pattern=“dd-MM-yyyy“ value=“${fecha_java_util_Date}“ />
<fmt:formatDate pattern=“dd“ value=“${fecha_java_util_Date}“ />
<input type="hidden" name="action" value="modificar" />
Una etiqueta <input type="hidden" /> nos permite enviar información en un formulario sin que dicha información aparezca en la vista.
Hospital
Hospital I
La base de datos tendrá 4 campos: id(PRIMARY KEY, AUTOINCREMENT), nombre (VARCHAR), apellidos (VARCHAR), fecha_alta(DATE).
Para convertir una fecha en un objeto de tipo Date:
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
Date fecha = formatter.parse(stringFecha);
//MM mayúscula, las minúsculas son para minutos.
Estas conversiones las haremos en el controlador (No modificaremos el bean paciente para añadirle las propiedades día, mes y año)
Para modificar la fecha primero convierto el tipo de dato recibido del formulario a un dato de tipo java.util.Date.
Luego, cuando le pase parámetros a la consulta sql convertiré esta fecha un dato de tipo java.sql.Date:
pstmt.setDate(3,new java.sql.Date(fecha.getTime()));
Hospital II
Necesitamos que el formulario de modificación haga dos cosas: actualice la base de datos y refresque los datos de los cuadros de texto. Para ello, nos vendrá bien el siguiente código javascript:
<script type="text/javascript">
function cambiarAction(){
document.getElementById("action").value="actualizaCampos";
document.getElementById("formulario").submit();
}
</script>
Ejercicio listado libros
Ejercicio listado libros I
Ejercicio listado libros II
Nota:
Pasarle parámetros al action de un formulario directamente en su URL funciona sólo si estamos enviando la información por POST. En caso contrario, la URL que definimos en el action y la que generamos dinámicamente entran en conflicto y los parámetros definidos explícitamente en el action del formulario no son enviados.
<form method="post" action="ServletController?action=alta">
Para programar la opción de agregar un nuevo registro, tenemos dos opciones:
- Podemos hacer un formulario que abarque sólo las celdas de la fila dónde se encuentra el botón de agregar (esta es la opción más sencilla, pero desde el punto de vista de la validación del código html sería incorrecta).
- Podemos hacer un formulario que abarque toda la tabla html donde se están mostrando los resultados de la consulta (esta opción es un poco más compleja, pero el código html validaría correctamente).
Ejercicio listado libros III
Lo más sencillo será utilizar un formulario para cada fila, de tal forma que abarque todas las celdas de cada registro.Esta opción no valida el código html.
Ejercicio listado libros IV
Podemos usar la siguiente función:
function modificar(id){
document.getElementById("action").value = "modificar";
document.getElementById("idEnviada").value = id;
document.getElementById("modifica_titulo").value = document.getElementById("titulo_"+id).value;
document.getElementById("modifica_precio").value = document.getElementById("precio_"+id).value;
document.getElementById("formulario").submit();
}
Ejercicio calidades
Para que las capas amarillas contenidas en la tabla aparezcan alineadas con la parte baja de la misma usaremos el siguiente estilo: <td style='vertical-align:bottom'>
Como utilizando JSTL y un solo bucle es posible generar la tabla de jugadores de la izquierda y la tabla de calidades de la derecha.
<c:forEach var="jugador" items="${requestScope.jugadores}">
<tr><td><c:out value="${jugador.numero_camiseta}" /></td><td><c:out value="${jugador.nombre}" /></td></tr>
<c:set var="fsup">
${fsup}<td style="vertical-align:bottom;">
<div style="background-color:yellow; width:20px; height:<c:out value="${jugador.calidad*30}" />px"></div></td>
</c:set>
<c:set var="finf">
${finf}<td><c:out value="${jugador.numero_camiseta}" /></td>
</c:set>
</c:forEach>
Ejercicio librería con sesión
Repetir el ejercicio de la librería cargando mediante un SessionListener los datos de la base de datos en un ArrayList que almacenaremos en la sesión. A partir de ahí, todas las operaciones las haremos contra dicha sesión.
En caso de terminar antes, intentar que los cuadros de modificación se actualicen al cambiar el valor de la combo.
Para ello, cuando la combo cambie, enviaré la id del libro, la compararé con la id de los libros almacenados en el arraylist y devolveré los datos del libro para el cual hubo coincidencia.
Ejercicio librería – con Map
Repetir el ejercicio de la diapositiva anterior utilizando un TreeMap para almacenar la información en vez de un ArrayList.
Métodos del TreeMap:
objetoTreeMap.put(Object key, Object value);
objetoTreeMap.remove(Object key);
objetoTreeMap.get(Object key);
objetoTreeMap.firstEntry().getValue();
Recorrer un Map:
<c:forEach var="libro" items="${sessionScope.libros}">
<option value="<c:out value="${libro.value.id}" />"
...
Ejercicio Login
Si el usuario se loguea con éxito, colocamos un bean del usuario logueado en la sesión. Y redirigimos a la página de ''usuario logueado'. En caso contrario, redirigimos a la página de''se ha producido un error''.
Ejercicio Mensajería
mensaje | |||
---|---|---|---|
id | remitente | destinatario | mensaje |
1 | 1 | 1 | Hola! |
2 | 1 | 2 | Qué pasa tron! |
3 | 1 | 1 | Hola Caracola |
4 | 1 | 2 | Te odio |
usuario | ||
---|---|---|
id | nombre | pass |
1 | pp | kk |
2 | kk | kk |
<input type="checkbox" name="arrayDeUsuarios" value="<c:out value="${usuario.id}"/>"/>
String[] arrayDeUsuarios = request.getParameterValues("arrayDeUsuarios");
Ejercicio foro
usuario | ||
---|---|---|
id | nombre | pass |
1 | pp | pp |
2 | kk | kk |
comentario | |||
---|---|---|---|
id | id_usuario | id_hilo | comentario |
1 | 2 | 7 | es super guay! |
2 | 2 | 8 | En pablomonteserin.com los vendes muy buenos |
hilo | |||
---|---|---|---|
id | id_usuario | nombre_hilo | texto_hilo |
7 | 1 | Escoger ordenador | Dónde comprar uno bueno |
8 | 1 | ¿Qué opinas de Ubuntu? | Dudas sobre Ubuntu |
Context
Hay un solo Context por cada aplicación web que hay en el servidor.
Lo que es propio de cada usuario va en sesión. Lo que es común a todos va en Context.
En un carrito de compra, el carrito de cada usuario se almacenaría en una variable sesión, mientras que la lista de precios estaría en una variable Context.
Variables de contexto
Acceder al contexto en el servlet:
ServletContext context = request.getSession().getServletContext();
Acceder al contexto en el listener:
ServletContext context = arg0.getServletContext();
Serialización
Consiste en convertir un objeto en una sucesión de bits o en un formato humanamente más legible como XML o JSON, entre otros.
La serialización es un mecanismo ampliamente usado para transportar objetos a través de una red, para hacer persistente un objeto en un archivo o base de datos, o para distribuir objetos idénticos a varias aplicaciones o localizaciones.
EscribirDatoMain
Persona p = new Persona();
SerializeObjecs.writeObject(p,"/home/monty/Documents/serializado.txt" )
LeerDatoMain
Persona p = (Persona)SerializeObjects.readObject("/home/monty/Documents/serializado.txt")
Ejercicio suma sesión y contexto
Hacer un jsp con dos campos, n1 y n2. Al pulsar en el botón sumar, el controlador procesará:
- la suma de request.
- la suma de sesión.
- la suma de contexto.
Haremos todas las operaciones en el controlador.
Con un listener, cuando baje el servidor guardaré la información de la suma del contexto en un archivo de texto. Al subir el servidor se recupera la información del archivo de texto.
Descargar SerializeObjects.javaEjercicio
Repetir el ejercicio de las calidades almacenando los equipos y los jugadores en el contexto. Almacenaré los equipos como un TreeMap y los jugadores como una Collection. Podré recuperar los jugadores de un equipo mediante un método getJugadores() de la clase Equipo. Dicho método devuelve una collection de jugadores.
El código de la derecha es parte del código que irá en el ContextListener para almacenar los jugadores en el contexto.
ArrayList jugadores = new ArrayList();
ResultSet rsJugadores = stm.executeQuery("select * from jugador order by equipo_cod, numero_camiseta");
boolean haySiguienteRegistro=rsJugadores.next();
Integer equipo_cod_actual = 1;
Integer equipo_cod_siguiente = 1;
while(haySiguienteRegistro){
Jugador jugador = new Jugador();
equipo_cod_actual = rsJugadores.getInt("equipo_cod");
jugador.setEquipo_cod(equipo_cod_actual);
jugador.setJugador_cod(rsJugadores.getString("jugador_cod"));
...
jugadores.add(jugador);
haySiguienteRegistro = rsJugadores.next();
if(haySiguienteRegistro){
equipo_cod_siguiente = rsJugadores.getInt("equipo_cod");
if(rsJugadores.getInt("equipo_cod") != equipo_cod_actual){
equipos.get(equipo_cod_actual).setJugadores(jugadores);
jugadores = new ArrayList();
}
}else{
equipos.get(equipo_cod_actual).setJugadores(jugadores);
}
}
ServletContext context = arg0.getServletContext();
context.setAttribute("equipos", equipos);
Upload file (subir fichero)
index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Insert title here</title></head>
<body>
<form method="POST" enctype='multipart/form-data' action="ServletController">
Por favor, seleccione el trayecto del fichero a cargar<br/>
<input type="file" name="fichero">
<input type="submit">
</form>
</body>
</html>
ServletController.java
public boolean procesaFicheros(HttpServletRequest req) {
try {
DiskFileUpload fu = new DiskFileUpload();// construimos el objeto que es capaz de parsear la pericion
fu.setSizeMax(1024*512); // máximo numero de bytes (512)
fu.setSizeThreshold(4096);// tamaño por encima del cual los ficheros son escritos directamente en disco
fu.setRepositoryPath("/tmp");// directorio en el que se escribirán los ficheros con tamaño superior al soportado en memoria
List fileItems = fu.parseRequest(req);// ordenamos procesar los ficheros
if(fileItems == null) {
System.out.println("La lista es nula");
return false;
}
Iterator i = fileItems.iterator();// Iteramos por cada fichero
FileItem actual = null;
while (i.hasNext()){
actual = (FileItem)i.next();
String fileName = actual.getName();
File fichero = new File(fileName);// construimos un objeto file para recuperar el trayecto completo
System.out.println("El nombre del fichero es " + fichero.getName());// nos quedamos solo con el nombre y descartamos el path
fichero = new File("/home/monty/Desktop/"+fichero.getName());// escribimos el fichero colgando del nuevo path
actual.write(fichero);
}
}catch(Exception e) {
System.out.println("Error de Aplicación " + e.getMessage());
return false;
}
return true;
}
Web Service
- Creamos un Dynamic Web Proyect webService_llamado.
- Creamos un paquete dentro del proyecto.
-
Creamos una clase llamada ClaseLlamada (llámala cómo quieras, pero no igual que el método) dentro de este proyecto con el siguiente método estático, que recibe un parámetro y devuelve el parámetro transformado.
public static String saludar(Strin parametro){ return "Hola " + parametro; }
- Para dar este paso es posible que sea necesario que el servidor esté arrancado, ya que a veces no se arranca automáticamente. Botón derecho sobre la clase recién creada → Web Service → Create Web Service → next → elijo los métodos que quiero publicar como web service(deberían ser métodos que devolviesen algo) → finish
- Creo un nuevo dynamic web proyect llamador.
- Creo un paquete dentro del src.
- Botón derecho sobre el paquete recién creado → new → web service client → browse → busco el fichero wsdl (webcontent/wsdl/saludar.wsdl) creado en el proyecto anterior → finish
- Creo una clase Main desde la que llamo al método que contiene la clase
public static void(String [] args){ ClaseLlamadaProxy claseLlamadaProxy = new ClaseLlamadaProxy(); try{ System.out.println(claseLlamadaProxy.saludar("Juan")); }catch(Remote Exception){ e.printStackTrace(); } }
El proyecto llamado debe estar desplegado en el servidor cuando lo llamemos.
Aviso Legal
Los derechos de propiedad intelectual sobre el presente documento son titularidad de D. Pablo Monteserín Fernández Administrador, propietario y responsable de pablomonteserin.com. El ejercicio exclusivo de los derechos de reproducción, distribución, comunicación pública y transformación pertenecen a la citada persona.
Queda totalmente prohibida la reproducción total o parcial de las presentes diapositivas fuera del ámbito privado (impresora doméstica, uso individual, sin ánimo de lucro).
La ley que ampara los derechos de autor establece que: “La introducción de una obra en una base de datos o en una página web accesible a través de Internet constituye un acto de comunicación pública y precisa la autorización expresa del autor”.
El contenido de esta obra está protegido por la Ley, que establece penas de prisión y/o multa, además de las correspondientes indemnizaciones por daños y perjuicios, para quienes reprodujesen, plagiaren, distribuyeren o comunicaren públicamente, en todo o en parte, o su transformación, interpretación o ejecución fijada en cualquier tipo de soporte o comunicada a través de cualquier medio.
El usuario que acceda a este documento no puede copiar, modificar, distribuir, transmitir, reproducir, publicar, ceder, vender los elementos anteriormente mencionados o un extracto de los mismos o crear nuevos productos o servicios derivados de la información que contiene.
Cualquier reproducción, transmisión, adaptación, traducción, modificación, comunicación al público, o cualquier otra explotación de todo o parte del contenido de este documento, efectuada de cualquier forma o por cualquier medio, electrónico, mecánico u otro, están estrictamente prohibidos salvo autorización previa por escrito de Pablo Monteserín.
El autor de la presente obra podría autorizar a que se reproduzcan sus contenidos en otro sitio web u otro soporte (libro, revista, e-book, etc.) siempre y cuando se produzcan dos condiciones:
- Se solicite previamente por escrito mediante email al correo pablomonteserin@pablomonteserin.com.
- En caso de aceptación, no se modifiquen los textos y se cite la fuente con absoluta claridad.
Una parte de las imágenes utilizadas en este documento no son propiedad de Pablo Monteserín, por lo que, si alguna de estas imágenes estuviera sujeta a derechos de autor, o a algún otro tipo de derecho que impida su publicación en este documento, una vez que el autor, Pablo Monteserín, tenga conocimiento del hecho, procederá a la retirada inmediata de la imagen protegida por los derechos pertinentes.