Conceptos básicos de servlets

 

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:

Además, se tendrá un campo oculto:

<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:

Además, se tendrá un campo oculto:

<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:

Además, se tendrá un campo oculto:

<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:

Además, se tendrá un campo oculto:

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

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

articulos.put("raton inalambrico",
              new ObjetoSubasta("pepe",
              "raton inalambrico", 12));
ObjetoSubasta obj = (ObjetoSubasta)
   (articulos.get("raton inalambrico"));
articulos.remove("raton inalambrico");

 

a.2) Al principio del método doGet(...) se deben tomar los parámetros de entrada, que serán:

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.

response.setContentType("text/html");
response.setHeader("Refresh", "5");

PrintWriter out = response.getWriter();
out.println ("<html>");
out.println ("<body>");
...
out.println ("<a href=\"/subasta/vender\">" + 
             "Vender un articulo</a>");
out.println ("<a href=\"/subasta/login\">" + 
             "Salir</a>");
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() + "] ");
   } 
}
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>