Control de navegación en Servlets
Históricamente, un Web es era servidor bastante sencillo que sirve para
proporcionar ficheros estáticos, normalmente imagenes y páginas HTML. Podeis
ver incluso, en otro de nuestros tutoriales, como crear tu propio WebServer
en Java
Cuando se extindió el uso y empezó a demandarse la creación de estas
páginas de un modo dinámico, a través del denominado CGI (Common Gateway
Interface), se parcheó el sistema para permitir de un modo sencillo permitir
ejecutar programas en el servidor y redirigir la salida por pantalla al cliente,
pasando las peticiones a través de variables de entorno (la famosa QUERY_STRING).
- Los que desarrollamos en su momento sistemas basados en CGI (sobre todo en
C/C++), teníamos que hacérnoslo todo a mano: - Leer las variables de entorno
- Parsear las variables de entorno
- Controlar la cantidad de instancias simultaneas de nuestro componente
- Crear las cabeceras adecuadas para utilizar y mantener cookies
- Saber trabajar con memoria compartida y semáforos (para comunicar
procesos) - Crearnos sistemas propietarios de plantillas (lo que ahora serían los
JSPs)
Menos mal, que a alguien se le ocurrió (y tenia medios para hacerlo) dividir
el esfuerzo en dos partes:
- La parte control de peticiones, filtrado de parámetros, gestión de
cookies y sesiones, etc. a través de un contenedor. - La parte de lógica pura de aplicación (capa de presentación)….
Esto es lo que ahora conocemos como servlets (en tecnología Java)
Un servlet es un componente que se ejecuta dentro de un contenedor Java y que
debe cumplir unas reglas (que lo consigue heredando de una clase base) para
poder ejecutarse en dicho contenedor.
Este contenedor Web se conecta al servidor Web como otro parche, utilizando
también la interfaz CGI …. por lo que todo lo que sabiamos, sigue siendo
útil y necesario cuando profundizamos en la tecnología.
Por lo tanto necesitamos tener un servidor Web y un contenedor de servlets
…… en este caso TOMCAT, nos proporciona ambas cosas en un mismo paquete.
Vamos a mostraros como desarrollar una aplicación basada en servlets y como
montar el esqueleto de una aplicación donde poder controlar la navegación y
poder tener separada la lógica de negocio y presentación. Para ello, podemos
usar NetBeans o Forte, que ya llevan integrados un servidor TOMCAT y sin
necesidad de hacer configuraciones adicionales, podemos probar lo que vamos
haciendo.
En otro tutorial, Aplicacionescon JSPs, podéis ver todas las pantallas para crear vuestra WebApp en Forte.
Vamos a partir de este punto.
Creamos un nuevo servlet
A través del asistente, creamos un servlet
Nuestro servlet será el encargado de controlar el Workflow de la
aplicación, haciendo ciertas validaciones e invocando a otros objetos para que
realicen el filtrado de los parámetros y la invocación de otros objetos de
negocio.
Rellenamos el resto de la información.
Y ya tenemos nuestra clase
import javax.servlet.*; import javax.servlet.http.*; public class servletsCentral extends HttpServlet { /** Initializes the servlet. } /** Destroys the servlet. /** Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods. out.println(«</body>»); /** Handles the HTTP <code>GET</code> method. /** Handles the HTTP <code>POST</code> method. /** Returns a short description of the servlet. } |
Sin saber mucho más, podemos ver que si cambiamos lo que hay en azul,
tenemos el espacio y un trozo de código de ejemplo para generar una página
dinámicamente.
Descomentamos el código, introducimos algo en el cuerpo y ejecutamos el
servlet …
out.println(«<body>»); out.println(«<center><h1>Primer Servlet</h1></center>»); out.println(«</body>»); |
Y ya estamos en marcha
Creación de sistema de acciones
Vamos a hacer una cosa, este servlet, vamos a querer usarlo para
muchas cosas … porque todas las aplicaciones tienen muchos servicios comunes
-
Control de seguridad
-
Contadores de peticiones
-
Estadísticas
Así que lo que podemos hacer, es crearnos unos objetos que
vamos a llamar acciones de tal modo que cuando algún usuario quiera
hacer algo en el sistema, llamará a este servlet y le dirá que acción quiere
ejecutar y los parámetros para ejecutarla. Nuestro servlet, controlará si el
usuario puede ejecutar dicha acción en base a dos cosas:
-
Que tiene permisos para poder ejecutarla
-
Que ha ejecutado anteriormente en nuestra aplicación alguna
otra acción obligatoria.
Del mismo modo, podríamos ir contabilizando la cantidad de
peticiones que se realizan a cada acción.
Si os poneis a pensarlo ¿hace falta mucho más para un
control de WorkFlow básico y para capturar información para un sistema básico
de CRM operacional?
Bueno, vamos a crear una clase base abstracta, denominada
CAccionBase, y todas las demás acciones deberán heredar de esta para poder
ejecutarse. OJO, vamos a hacerlo simple y tratando de hacerlo medianamente bien,
aunque este código no debe servir de referencia … sobre todo, porque es un
paso intermedio para mostrar como llegar a donde queremos.
Estas acciones no poseerán la lógica
de negocio de la aplicación ….. solo se encargarán de invocar a
los componentes que realmente la posean y obtener grupos de variables sencillas,
que presentaremos en formato HTML a nuestros usuarios.
En resultado es (en rojo la añadido a mano):
import javax.servlet.*; import javax.servlet.http.*; public abstract class CAccionBase { public String Descripcion = «Esta debe ser la descripción de la acción«; /** Creates a new instance of CAccionBase */ boolean procesaAccion(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { } |
Vamos a crear dos acciones:
- mostrarhome (que será la de por defecto)
- mostrarlistado (que mostrará una lista de links)
import javax.servlet.*; import javax.servlet.http.*; /** * * @author Roberto Canales */ public class CMostrarHome extends CAccionBase { /** Creates a new instance of CMostrarHome */ boolean procesaAccion(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException |
Ahora creamos la clase que responderá a la acción mostrar listado
import javax.servlet.*; import javax.servlet.http.*; /** * * @author Roberto Canales */ public class CMostrarListado extends CAccionBase { /** Creates a new instance of CMostrarHome */ boolean procesaAccion(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException out.println(«<a href=\»https://adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=seguridadtomcat\»>Seguridad en Tomcat</a><br>»); out.println(«</body>»); } |
Tambien, vamos a crear una acción para especificar un estado
incorrecto
import javax.servlet.*; import javax.servlet.http.*; public class CAccionInvalida extends CAccionBase { /** Creates a new instance of CMostrarHome */ boolean procesaAccion(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException |
Y ahora vemos la modificación del servlet, aunque los vamos a
explicar a trozos.
La idea es que cuando alguien realice una petición, comprobemos
si la acción que pide es válida o no y si ha ejecutado antes la acción
deseada por nosotros.
La petición que no hará será del tipo:
http://localhost:8081/servlet/servletsCentral?accion=mostrarlistado
Nosotros debemos saber leer este parámetro que se hace con:
String accionActual = request.getParameter(«accion»);
Para saber si el usuario a ejecutado antes la acción deseada,
debemos poder escribir en la sesión (para que se arrastren los datos entre cada
petición). Esto se hace con:
HttpSession sesionActual = request.getSession();
sesionActual.setAttribute(«ultimaaccion»,ultimaAccion);
y debemos saber leer el valor
HttpSession sesionActual = request.getSession();
Object ultimaAccionAlmacenado = sesionActual.getAttribute(«ultimaaccion»);
En función de la acción que nos pidan, crearemos el objeto
adecuado y ejecutaremos la petición (tambien validamos si se puede ejecutar):
if (accionActual.compareTo(«mostrarhome«) == 0)
{
return new CMostrarHome();
}
else if (accionActual.compareTo(«mostrarlistado«) == 0)
{
if (accionAnterior.compareTo(«mostrarhome») != 0)
{
return new CAccionInvalida();
}
return new CMostrarListado();
}
else
{
return new CAccionInvalida();
}
Y todo el código junto (organinado en metodos)
import javax.servlet.*; import javax.servlet.http.*; /** /** Initializes the servlet. }
/** Destroys the servlet. } boolean escribeEnSesionUltimaAccionValida(HttpServletRequest request,String ultimaAccion) String leeDeSesionUltimaAccionValida(HttpServletRequest request) if (ultimaAccionAlmacenado == null) return ultimaAccionAlmacenado.toString();
CAccionBase FactoriaAcciones(String accionActual, String accionAnterior) if (accionActual.compareTo(«mostrarhome») == 0) return new CMostrarListado(); }
protected void processRequest(HttpServletRequest request, HttpServletResponse response) if(accionActual == null) String accionAnterior = leeDeSesionUltimaAccionValida(request); if (!(accion instanceof CAccionInvalida)) accion.procesaAccion(request,response); /** Handles the HTTP <code>GET</code> method. /** Handles the HTTP <code>POST</code> method. /** Returns a short description of the servlet. } |
Probamos invocar nuestro servlet y como no especificamos la
acción, nos muestra un error.
Si invocamos la acción mostrar Home
Y si pinchamos el link, vemos los tutoriales.
Si tratamos de acceder a esta acción sin haber pasado por la home…
obtendremos un error.
Bueno, creo que básicamente hemos entendido la idea.
Creamos los JSPs
Ahora, si nos ponemos a pensar un poco….. podríamos mejorarlo
un poquito … podemos usar las acciones para ejecutar la lógica de negocio sin
pintar nada y delegar el pintado a un JSP.
Gracias a los JSPs, las páginas serían mucho más sencillas de
construir y podríamos dividir nuestros equipos de desarrollo en 2:
-
Programadores de acciones y lógica de negocio
-
Programadores de la capa de presentación
Solo tienen que poner se acuerdo en los objetos que deben
intercambiar. Esto puede garantizar una buena separación entre lógica de
negocio y presentación.
De este modo estamos aplicando el patrón ModeloVistaControlador
….(ver tutorial JSPsy MVC)
En todos ellos incluimos un trozo de código para comprobar que la acción se
a ejecutado y que hemos sido capaces de pasar parámetros desde la acción al
JSP. <% out.print (request.getAttribute(«accion»).toString()); %>
JSP mostrarerror.jsp
<%@page contentType=»text/html»%> <html> <head> <title>Servlet</title> </head> <body> <center><h1>La accion es incorrecta</h1> <br><BR>Vengo de la acción: <b> <% out.print (request.getAttribute(«accion»).toString()); %> </b> </center> </body> </html> |
JSP mostrarhome.jsp
<%@page contentType=»text/html»%> <html> <head> <title>Servlet</title> </head> <body> <center><h1>Pagina principal</h1> <a href=»servletsCentral?accion=mostrarlistado»>pinchar para mostrar listado</a> <br><BR>Vengo de la acción: <b> |
JSP mostrarlistado.jsp
<%@page contentType=»text/html»%> <html> <head> <title>Servlet</title> </head> <body> <center><h1>Otros tutoriales</h1><br> <a href=»../tutoriales/tutoriales.php?pagina=seguridadtomcat»>Seguridad en Tomcat</a><br> <a href=»../tutoriales/tutoriales.php?pagina=jspbasico»>SAplicaciones con Jsps</a><br> <a href=»../tutoriales/tutoriales.php?pagina=taglibs»>Librería de etiquetas en JSPs (Tag-libs)</a><br> <br><BR>Vengo de la acción: <b> </center> |
Y ahora modificamos nuestras acciones … aunque solo vamos a mostrar una
porque las demás son iguales.
Lo que hacemos es enviar una cadena de caracteres a través del objeto
petición
request.setAttribute («accion»,
«mostrarhome»);
import javax.servlet.*; import javax.servlet.http.*; /** * * @author Administrator */ public class CMostrarHome extends CAccionBase { /** Creates a new instance of CMostrarHome */ boolean procesaAccion(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException |
Ahora vemos el servlet y marcamos en verde lo modificado
import javax.servlet.*; import javax.servlet.http.*; public class servletsCentral extends HttpServlet { /** Initializes the servlet. }
/** Destroys the servlet. } boolean escribeEnSesionUltimaAccionValida(HttpServletRequest request,String ultimaAccion) String leeDeSesionUltimaAccionValida(HttpServletRequest request) if (ultimaAccionAlmacenado == null) return ultimaAccionAlmacenado.toString();
CAccionBase FactoriaAcciones(String accionActual, String accionAnterior) if (accionActual.compareTo(«mostrarhome») == 0) return new CMostrarListado(); String FactoriaJSPs(String accionActual, String accionAnterior) protected void processRequest(HttpServletRequest request, HttpServletResponse response) if(accionActual == null) String accionAnterior = leeDeSesionUltimaAccionValida(request); CAccionBase accion = FactoriaAcciones(accionActual,accionAnterior); if (!(accion instanceof CAccionInvalida)) accion.procesaAccion(request,response); String jspPresentacion = FactoriaJSPs(accionActual,accionAnterior); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) /** Handles the HTTP <code>POST</code> method. /** Returns a short description of the servlet. } |
Y si volvemos a navegar por las páginas, podremos ver que el JSP es quien
resuelve la página y es capaz de recuperar los datos deseados.
Podemos ver que esto va cogiendo forma ……
:Ahora, solo tendríamos que mejorar un poquito el sistema para que no este
tan acopladas al código:
- Nombre de las acciones
- Clase de la accion
- Jsp de presentación
- E incluso contadores (para estadísticas y CRM)
Para ello, podemos crear una clase que se encargue de retornarnos esta
información … que almacenaremos en un fichero o base de datos …… pero
esto …… es otra historia ….