Como ejercicio de servlets haremos un servidor de subastas. Dicho servidor permitirá que varios clientes se conecten a él, y virtualmente puedan ofertar sus productos y pujar por los de otros clientes.
Para esta aplicación, tenemos una infraestructura con 5 servlets:
Figura 1. Esquema de aplicación de subastas
Vemos en la estructura que se tienen 4 servlets "periféricos" (ServletRegistro, ServletLogin, ServletComprar y ServletVender), y uno central ServletMain que canaliza todas las operaciones. Los 4 servlets periféricos simplemente muestran un formulario, que al validarse llamará a ServletMain con unos determinados parámetros. Luego, ServletMain se encargará de realizar la acción correspondiente de acuerdo a los parámetros que recibe.
Se proporciona un fichero ZIP con el esqueleto de los 5 servlets, que iremos desarrollando a lo largo de los ejercicios. También se tiene un fichero descriptor (web.xml) que se irá completando a continuación.
1. (SUBASTA) Como primer ejercicio, vamos a definir el fichero descriptor de la aplicación (web.xml), mapeando los 5 servlets propuestos. Se tiene un esqueleto de fichero descriptor, con el servlet ServletRegistro ya añadido:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"> <web-app> <display-name>Aplicacion de Subastas</display-name> <description> Aplicacion de Subastas con Servlets </description> <servlet> <servlet-name>sregistro</servlet-name> <servlet-class>ServletRegistro</servlet-class> </servlet> <servlet-mapping> <servlet-name>sregistro</servlet-name> <url-pattern>/registro</url-pattern> </servlet-mapping> </web-app>
De forma que para llamar al servlet ServletRegistro lo llamaremos mediante:
http://servidor:puerto/subasta/registro
Faltaría añadir los otros 4 servlets:
<servlet> <servlet-name>slogin</servlet-name> <servlet-class>ServletLogin</servlet-class> </servlet> <servlet-mapping> <servlet-name>slogin</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
<servlet-mapping> <servlet-name>slogin</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
2. (SUBASTA) Ahora vamos a desarrollar los servlets "periféricos", esto es, los servlets ServletRegistro, ServletLogin, ServletVender y ServletComprar, que no tienen más que un formulario cuyo submit llama al servlet central. Veremos a continuación las características de cada formulario.
a) ServletRegistro
El servlet ServletRegistro servirá, como veíamos, para que un usuario se dé de alta en las subastas. Este servlet se da ya hecho como ejemplo, y simplemente muestra un formulario con 3 campos de texto:
- login: para el login del nuevo usuario
- password: para el password del nuevo usuario
- email: para el e-mail del usuario
Además, se tendrá un campo oculto:
- accion: con la acción que realizará el servlet central cuando le llegue esta petición. Dicha acción es, en este caso, registrar.
<form action="/subasta/main"> <input type="hidden" name="accion" value="registrar"> Login: <input name="login" value=""><br> Password: <input name="password" value=""><br> E-mail: <input name="email" value=""><br> <input type="submit" value="Enviar"> </form>Opcionalmente, podemos añadir a la página un enlace a ServletLogin, por comodidad:
<a href="/subasta/login">Validacion</a>b) ServletLogin
El servlet ServletLogin permitirá a un usuario registrado entrar a la subasta. Mostrará un formulario con 2 campos de texto:
- login: login del usuario
- password: password del usuario
Además, se tendrá un campo oculto:
- accion: con la acción que realizará el servlet central cuando le llegue esta petición. Dicha acción es, en este caso, validar.
<form action="/subasta/main"> <input type="hidden" name="accion" value="validar"> Login: <input name="login" value=""><br> Password: <input name="password" value=""><br> <input type="submit" value="Enviar"> </form>Opcionalmente, podemos añadir a la página un enlace a ServletRegistro, para que el usuario no registrado se dé de alta:
<a href="/subasta/registro">Registrar</a>c) ServletVender
El servlet ServletVender permitirá a un usuario poner a la venta un artículo. Mostrará un formulario con 2 campos de texto:
- producto: descripción del producto que se ofrece
- valor: valor del producto (en euros)
Además, se tendrá un campo oculto:
- accion: con la acción que realizará el servlet central cuando le llegue esta petición. Dicha acción es, en este caso, vender.
<form action="/subasta/main"> <input type="hidden" name="accion" value="vender"> Producto: <input name="producto" value=""><br> Valor: <input name="valor" value=""><br> <input type="submit" value="Enviar"> </form>Opcionalmente, podemos añadir a la página un enlace a ServletMain, en el caso de que el usuario decida finalmente no vender:
<a href="/subasta/main">Volver</a>d) ServletComprar
El servlet ServletComprar permitirá a un usuario pujar por los productos que ofrecen otros usuarios. Se tendrá un parámetro de entrada, con el producto por el que se puja:
String producto = request.getParameter("producto");Se mostrará un formulario con 1 campos de texto:
- valor: valor que ofrece por el artículo seleccionado.
Además, se tendrá un campo oculto:
- accion: con la acción que realizará el servlet central cuando le llegue esta petición. Dicha acción es, en este caso, comprar.
<form action="/subasta/main"> <input type="hidden" name="accion" value="comprar"> Valor: <input name="valor" value=""><br> <input type="submit" value="Enviar"> </form>Opcionalmente, podemos añadir a la página un enlace a ServletMain, en el caso de que el usuario decida finalmente no pujar:
<a href="/subasta/main">Volver</a>
Tras esto, tendremos ya los 4 formularios periféricos de la aplicación. No podremos probarlos hasta no tener el núcleo central, pero podremos cargarlos para ver su apariencia:
http://localhost:8080/subasta/login http://localhost:8080/subasta/registro http://localhost:8080/subasta/comprar http://localhost:8080/subasta/vender
3. (SUBASTA) Finalmente, nos queda definir el servlet central ServletMain. Hay algunos elementos que se dejan ya implementados, para facilitar la tarea y para no perder tiempo con aspectos de la aplicación que son secundarios en cuanto a manejo de servlets se refiere. Veremos primero qué es lo que ya hay hecho, y después lo que se pide hacer.
Elementos ya implementados:
Vector usuarios = new Vector();que mantiene un listado de los usuarios que hay actualmente registrados en el servidor de subastas.
- Los objetos del vector usuarios son de tipo Usuario, una subclase interna que mantiene los datos de cada usuario registrado: login, password y e-mail.
- La clase ServletMain tiene los métodos:
boolean validaUsuario(String login, String password) boolean registraUsuario(String login, String password, String email) Usuario buscaUsuario(String login)Con validaUsuario() podremos hacer que un usuario registrado entre al servidor con el login login y password password. Devuelve true si se ha validado bien, false si no.
Con registraUsuario() podremos registrar un nuevo usuario, dando su login, password y e-mail. Devuelve true si se ha registrado bien, false si no (porque el login esté repetido, por ejemplo).
El método buscaUsuario() lo utilizan internamente los otros dos, para buscar el usuario de login login en la lista de usuarios. Devuelve el objeto Usuario con los datos del usuario, si se ha encontrado, o null si no se encontró.
Se tienen métodos para acceder a todos los campos, y un método:
void setPuja(String usuario, double valor)
que asigna un nuevo valor para el producto, ofrecido por el usuario usuario. Se empleará para ir incrementando el valor de la puja a medida que los usuarios vayan incrementando el valor ofrecido.
Se pide:
a) Ir añadiendo las funcionalidades restantes del servlet central:
a.1) Para llevar un control de los artículos que hay en cada momento en subasta, se puede utilizar por ejemplo un objeto Hashtable. De esta forma, tendremos un campo en la clase:
Hashtable articulos = new Hashtable();Tendremos los métodos put(...), get(...) y remove(...) de la clase Hashtable para añadir (o modificar), obtener y eliminar objetos de la subasta, respectivamente.
- Si queremos añadir o modificar el artículo "raton inalambrico", del usuario "pepe", con valor de 12 euros, simplemente haremos:
articulos.put("raton inalambrico", new ObjetoSubasta("pepe", "raton inalambrico", 12));
- Si queremos obtener los datos del artículo "raton inalambrico", sólo tendremos que hacer:
ObjetoSubasta obj = (ObjetoSubasta) (articulos.get("raton inalambrico"));
- Finalmente, si queremos eliminar el artículo "raton inalambrico", sólo tendremos que hacer:
articulos.remove("raton inalambrico");
a.2) Al principio del método doGet(...) se deben tomar los parámetros de entrada, que serán:
- login: el login del usuario que accede
- password: el password del usuario
- accion: la acción que desea realizar el usuario (introducida en los cuatro formularios periféricos construidos antes).
String login = request.getParameter("login); String password = request.getParameter("password"); String accion = request.getParameter("accion");Si alguno de los parámetros de entrada es null, se redirige al servlet ServletLogin:
response.sendRedirect("/subasta/login");
a.3) Si están todos los parámetros, vamos comprobando qué acción hay que realizar.
registrar: si la acción es registrar, obtenemos el email del usuario (el login y password ya los hemos tomado antes), e intentamos llamar al método registraUsuario(...) de ServletMain. Si funciona bien redirigimos a ServletLogin para que entre, y si no, redirigimos a ServletRegistro para que vuelva a introducir los datos:
String email = request.getParameter("email"); if (registraUsuario(login, password, email)) response.sendRedirect("/subasta/login"); else response.sendRedirect("/subasta/registro");
validar: si la acción es validar, intentamos llamar al método validaUsuario(...) de la clase ServletMain. Si no funciona, redirigimos a ServletLogin para que se vuelva a validar (si funciona no hace falta que hagamos nada).
if (!validaUsuario(login, password)) response.sendRedirect("/subasta/login");
comprar: si la acción es comprar, se toman primero dos parámetros de entrada más: el producto que se compra y el valor que se ofrece:
String producto = request.getParameter("producto"); double valor = Double.parseDouble( request.getParameter("valor"));Luego, tomamos el artículo, vemos si el valor ofertado es mayor que el actual, y si es así modificamos la puja. Luego redirigimos a este mismo servlet, con accion=validar, para que vuelva a validar al usuario:
ObjetoSubasta obj = (ObjetoSubasta) articulos.get(producto); if (obj.getValor() < valor) { obj.setPuja(login, valor); articulos.put(producto, obj); } response.sendRedirect("/subasta/main?accion=validar");
vender: si la acción es vender, se toman primero dos parámetros de entrada más: el producto que se pone en venta y el valor inicial:
String producto = request.getParameter("producto"); double valor = Double.parseDouble( request.getParameter("valor"));Luego añadimos el artículo a la lista, y redirigimos a este mismo servlet, con accion=validar, para que vuelva a validar al usuario:
articulos.put(producto, new ObjetoSubasta( login, producto, valor)); response.sendRedirect("/subasta/main?accion=validar");
Adicionalmente, tenemos otras dos acciones, generadas desde el propio servlet ServletMain:
adjudicar: se utiliza para otorgar un artículo a un usuario. Toma primero un parámetro de entrada, el producto que se adjudica:
String producto = request.getParameter("producto");Luego eliminamos el producto de la lista de artículos de la subasta, y redirigimos a este mismo servlet, con accion=validar:
articulos.remove(producto); response.sendRedirect("/subasta/main?accion=validar");cancelar: se utiliza para eliminar un artículo de la subasta, sin otorgarlo a ningún usuario. Toma primero un parámetro de entrada, el producto que se elimina:
String producto = request.getParameter("producto");Luego eliminamos el producto de la lista de artículos de la subasta, y redirigimos a este mismo servlet, con accion=validar:
articulos.remove(producto); response.sendRedirect("/subasta/main?accion=validar");Aunque en apariencia estas dos acciones hacen lo mismo, en adjudicar se podrían tomar los datos del usuario al que se le adjudica el producto, o el valor por el que se vende, y sacar esa información por pantalla. Esto se deja como opcional.
a.4) Finalmente, se muestra una página con el estado actual de la subasta.
- La página deberá enviar una cabecera Refresh que actualice el contenido cada cierto tiempo (5 segundos, por ejemplo):
response.setContentType("text/html"); response.setHeader("Refresh", "5"); PrintWriter out = response.getWriter(); out.println ("<html>"); out.println ("<body>"); ...
- Se mostrarán enlaces para vender un producto (enlace a ServletVender), y para salir de las subastas (enlace a ServletLogin):
out.println ("<a href=\"/subasta/vender\">" + "Vender un articulo</a>"); out.println ("<a href=\"/subasta/login\">" + "Salir</a>");
- Luego se mostrará una lista con los artículos del usuario actual, para que vea cómo van las pujas por ellos. Para cada artículo, se tendrá un enlace para adjudicar el artículo al usuario que más ha dado por él hasta ahora (llamando a este mismo servlet con accion=adjudicar), o para cancelar la puja (llamando a este mismo servlet con accion=cancelar):
Enumeration elem = articulos.elements(); while (elem.hasMoreElements()) { ObjetoSubasta os = (ObjetoSubasta) (elementos.nextElement()); if (os.getPropietario().equals(login)) { out.println("<br><a href=\"/subasta/main?" + "accion=adjudicar&producto=" + os.getProducto() + "\">Adjudicar</a> " + "<a href=\"/subasta/main?" + "accion=cancelar&producto=" + os.getProducto() + "\">Cancelar</a> " + os.getProducto() + " (" + os.getValor() + ") [" + os.getUsuario() + "] "); } }
- Finalmente, se mostrará una lista con los artículos de otros usuarios, para que el usuario actual pueda pujar por ellos. Para cada uno se tendrá un enlace a ServletComprar.
Enumeration elem = articulos.elements(); while (elem.hasMoreElements()) { ObjetoSubasta os = (ObjetoSubasta) (elementos.nextElement()); if (!os.getPropietario().equals(login)) { out.println("<br><a href=\"/subasta/comprar? + "producto=" + os.getProducto() + "\">Comprar</a> " + os.getProducto() + " (" + os.getValor() + ") [" + os.getUsuario() + "] "); } }
a.5) Notar que, con la estructura que le estamos dando a la aplicación, los datos del usuario validado (login y password) se pierden en las redirecciones. Para poderlos ir manteniendo a lo largo de la sesión del usuario, necesitaremos algunos parámetros adicionales en las llamadas a los servlets, que podremos pasar en la línea de petición GET:
<a href="/subasta/main?login=...&password=...">o como campos ocultos en los formularios periféricos:
<input type="hidden" name="login" value="<login>"> <input type="hidden" name="password" value="<password>">donde <login> y <password> se sustituirán en el código por las variables donde se encuentren el login y password tomados, respectivamente:
String login = request.getParameter("login"); String password = request.getParameter("password");
NOTA: observad que para las acciones comprar, vender, adjudicar y cancelar se vuelve a llamar al servlet ServletMain con accion=validar. Esto es porque, como se envía una cabecera Refresh para actualizar cada 5 segundos, si no recargamos la página con una accion distinta, se volverá a comprar, vender, adjudicar o cancelar el artículo repetidamente. Necesitamos redirigir con otra acción para que estas no se repitan. Así cada 5 segundos se valida la página y los datos del usuario.
b) Opcionalmente, se puede añadir una tabla en la página que muestre ServletMain donde se muestren las últimas N acciones realizadas por los usuarios. Para ello podemos tener en la clase los campos:
final int MAXOPERACIONES = 10; Vector ultimasOperaciones = new Vector();
Se trataría de mantener MAXOPERACIONES cadenas de texto en el vector ultimasOperaciones, insertando las últimas operaciones que se vayan produciendo, y eliminando las antiguas. Luego, para las operaciones actuales, se colocarían en una tabla, una en cada fila:
ultimasOperaciones.addElement("Venta de...");
ultimasOperaciones.removeElementAt(0);
<table border="0"> <th>ULTIMAS OPERACIONES</th> <tr><td>Operacion 1</td></tr> <tr><td>Operacion 2</td></tr> ... <tr><td>Operacion MAXOPERACIONES</td></tr>