1. Modelo Vista Controlador (modelo 2)
  2. Procesar toda la operativa en doGet o doPost
  3. Definir constantes de aplicación en el web.xml
  4. Ejercicios MVC
    1. Ejercicio - Lista de invitados
    2. Ejercicio fútbol
    3. Trabajo con fechas
    4. Input type hidden
    5. Hospital
    6. Listado libros
    7. Ejercicio calidades
    8. Ejercicio librería con sesión
    9. Ejercicio Login
    10. Ejercicio mensajeria
    11. Ejercicio foro
  5. Contexto
  6. Upload file
  7. 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.
ejemplo del flujo de vida de una aplicación que implementa el paradigma del modelo-vista-controlador

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.javaprotected 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);
}
Serviciopublic 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.

ejemplo de como debería quedar el ejercicio de la calculadora con Java

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.javapublic 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}

Evitar una reinserción en la base de datos al refrescar la web

if (session.getAttribute("recordInsertedSuccessfully") == null ){
	servicio.alta(libro);
	session.setAttribute("recordInsertedSuccessfully","true");
} else {
	session.setAttribute("recordInsertedSuccessfully",null);
}

Ejercicios MVC

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.

lista invitadosDescargar ejercicio invitados

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.

Pantallazo ejercicio resuelto

Trabajo con fechas

Paso de String a Date:SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
Date fecha = s.parse("10-12-1982");
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)
Nota:
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

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()));
pantallazo ejercicio hospital IDescargar ejercicio hospital

Hospital II

Necesitamos que el formulario de modificación haga dos cosas: actualice la base de datos y elimine el registro que hemos seleccionado. Para ello deberemos añadir un botón de eliminar, así como el siguiente código Javascript

<script type="text/javascript">
function cambiarAction(){
        document.getElementById("action").value="actualizaCampos";
        document.getElementById("formulario").submit();
}
</script>

Ejercicio listado libros

Descargar librería resuelta

Ejercicio listado libros I

Pantallazo ejercicio listado libros 1

Ejercicio listado libros II

pantallazo listado libros

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

Pantallazo ejercicio listado libros

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

Pantallazo ejercicio listado libros 4

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();
}

<form id="formulario"> <table> <tr> <td>${libro.id}</td> <td><input type="text" value="fulanito" id="nombrecampo${libro.id}"></td> <td><input type="button" value="enviar" onclick="modificar(${libro.id})"></td> </tr> <tr> <td>2</td> <td><input type="text" value="menganito" id="nombrecampo2"></td> <td><input type="button" value="enviar" onclick="modificar(2)"></td> </tr> </table> <input type="hidden" name="id_libro" id="id_libro"> <input type="hidden" name="nombre" id="nombre"> </form> <script type="text/javascript"> function modificar(id_libro){ alert(id_libro) document.getElementById("id_libro").value=id_libro; var nombreAModificar = document.getElementById("nombrecampo"+id_libro).value; document.getElementById("nombre").value=nombreAModificar; document.getElementById("formulario").submit(); } </script>

Ejercicio calidades

Pantallazo listado jugadores

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.

librería mvc librería mvc 2

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

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

ejercicio mensajería
mensaje
idremitentedestinatariomensaje
111Hola!
212Qué pasa tron!
311Hola Caracola
412Te odio
usuario
idnombrepass
1ppkk
2kkkk
		
<input type="checkbox" name="arrayDeUsuarios" value="<c:out value="${usuario.id}"/>"/>
String[] arrayDeUsuarios = request.getParameterValues("arrayDeUsuarios");
		
	

Ejercicio foro

ejercicio foro
usuario
idnombrepass
1pppp
2kkkk
comentario
idid_usuarioid_hilocomentario
127es super guay!
228En pablomonteserin.com los vendes muy buenos
hilo
idid_usuarionombre_hilotexto_hilo
71Escoger ordenadorDónde comprar uno bueno
81¿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.

Ejercicio

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

  1. Creamos un Dynamic Web Proyect webService_llamado.
  2. Creamos un paquete dentro del proyecto.
  3. 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;
    		}
    				
    			
  4. 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
  5. Creo un nuevo dynamic web proyect llamador.
  6. Creo un paquete dentro del src.
  7. 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
  8. 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();
    	}
    }
    	
    
Notas:
El proyecto llamado debe estar desplegado en el servidor cuando lo llamemos.
icono de mandar un mail¡Contacta conmigo!
contacta conmigoPablo Monteserín

¡Hola! ¿En qué puedo ayudarte?