Servlets y JSP


    Programación Distribuida y su Aplicación Bajo Internet
    Curso: 2004-2005
    Autor: Juansa Sendra

Contents

1 Introducción
1.1 Compilación, instalación, e invocación servlets
1.2 Ciclo de vida de los servlets
1.3 HTTP
1.4 Generación HTML
2 Formularios HTML
2.1 Lectura de datos del formulario
2.2 Gestión de GET y POST con un único servlet
2.3 Validación de campos (sin contenido o contenido erróneo)
2.4 Esquemas de gestión de formularios
3 Gestión de cookies y sesiones
3.1 Uso de cookies. Ventajas e inconvenientes
3.2 Envío de cookies y lectura de cookies
3.3 Concepto de sesión
4 JSP
4.1 Bloques y Expresiones
4.2 Variables predefinidas
4.3 Directivas y declaraciones
4.4 Procesamiento de formularios
5 Acceso a base de datos con JDBC
5.1 Conceptos básicos
5.2 Registro de drivers JDBC
5.3 URLs para JDBC
5.4 Uso de la conexión
5.5 Interrogación básica
5.6 Transacciones
5.7 Pasos para la puesta en marcha
5.8 Ejemplos
[ back to top ]

1 Introducción

Los 'servlets' son módulos de código Java que se ejecutan como una aplicación servidor (normalmente como extensión de un servidor web) para responder a solicitudes de los clientes. Aunque el concepto no está limitado a un único protocolo cliente/servidor, se usan principalmente con http (HttpServlet).

Los servlets utilizan las clases definidas en el paquete javax.servlet (entorno básico) y javax.servlet.http (extensiones del entorno básico para servlets que responde a peticiones http). Gracias a la portabilidad de Java y el uso de un entorno estandard, los servlets son independientes del sistema operativo y del servidor concretos.

Usos típicos de los servlets:

Ventajas sobre los CGI:

1.1 Compilación, instalación, e invocación servlets

El primer paso es instalar JDK 1.2 o posterior (se recomienda JDK 1.3 o JDK 1.4). Posteriormente se instala Tomcat 4.0 o posterior (ambos pasos se describen en la documentación de la práctica inicial)

1.1.1 Configuración Tomcat

1.1.1.1 Habilitar el contexto ROOT

El contexto ROOT es la aplicación web por defecto. Para habilitarlo hay que editar el fichero server.xml del directorio conf, y eliminar el comentario que invalida la línea

    <Context path="" docBase="ROOT" debug="0"/>
1.1.1.2 Habilitar la invocación de servlets

El siguiente cambio simplifica el desarrollo de servlets (simplemente almacenamos el servlet X en WEB-INF/classes y utilizamos la URL http://host/servlet/X) en lugar de tener que editar WEB-INF/web.xml cada vez que añadimos un servlet. Hay que editar el fichero web.xml del directorio conf, quitando el comentario al elemento servlet-mapping

    <servlet-mapping>
        <servlet-name>invoker</servlet-name>
        <url-pattern>/servlet/*</url-pattern>
    </servlet-mapping>
1.1.1.3 Cambiar al port 80

Si no tenemos otros servidores escuchando en el port 80, indicamos a Tomcat que escuche en dicho port (evita tener que indicar el port en cada URL). En ciertos sistemas se necesitan permisos de administración para realizar el cambio (en Windows NT tendríamos que deshabilitar previamente el IIS). Para cambiar el port se edita el fichero server.xml del directorio conf, cambiando el atributo port del elemento Connector de 8080 a 80

    <Connector
        className="org.apache.coyote.tomcat4.CoyoteConnector" port="80" .. />
1.1.1.4 Habilitar recarga servlets

Para no tener que arrancar de nuevo Tomcat cada vez que recompilamos un servlet que ya estaba cargado en memoria, indicamos que se comprueben las fechas de modificación de los ficheros class solicitados, y se recarguen en memoria los que han sido modificados desde el momento en que se cargaron inicialmente en memoria.

Para ello editamos el fichero server.sml en el directorio conf, y añadimos al elemento Service un subelemento DefaultContext con el atributo 'reloadable' a cierto. En otras palabras, buscamos el comentario

    <!-- Define properties for each web application. This is only needed -4-. -->

y añadimos a continuación la siguiente línea:

    <DefaultContext reloadable="true"/>
1.1.1.5 Fijar la variable JAVA_HOME

Dicha variable indica a Tomcat el directorio de instalación del JDK, y le permite gestionar las páginas JSP. Para ello editamos el fichero catalina.bat del directorio bin, e insertamos el código

    if not "%JAVA_HOME%" == "" gotJavaHome
    set JAVA_HOME=-4-       // esta es la nueva linea. Sustituir -4- por el directorio (ej c:\jdk1.3)
    :gotJavaHome
1.1.1.6 Cambiar settings memoria DOS

Sólo resulta necesario en versiones antiguas de Windows, en las que podemos recibir el mensaje "Out of Environment Space" al arrancar el servidor. Hay que pulsar el botón derecho sobre el fichero startup.bat del directorio bin, seleccionar Propiedades/Memoria, y cambiar 'Entorno inicial' de 'auto' a 2816. Repetir el proceso con el fichero shutdown.bat del directorio bin

1.1.2 Test servidor

1.1.2.1 Verificar el arranque del servidor

Lanzar startup.bat, e iniciar un navegador en http://localhost (o http://localhost:8080 si no hemos cambiado al port 80). Debe aparecer una página de bienvenida

1.1.2.2 Comprobar el acceso a nuestras propias páginas HTML y JSP

Con ello verificamos en qué directorios descargar pags HTML y la activación del compilador Java.

Debemos situar las pags HTML y JSP en webapps/ROOT o webapps/ROOT/loQueSea, y acceder con la URL http://localhost/fichero o http://localhost/loQueSea/fichero.

Generamos los siguientes ficheros:

hola.html

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
    <head><title>Primera prueba HTML</title>
        <body>
        <h1>Prueba HTML</h1>
        Un saludo a todos
        </body>
    </html>

hola.jsp

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
    <head><title>Primera prueba JSP</title>
        <body>
        <h1>Prueba JSP</h1>
        Hora local: <%= new java.util.Date() %>
        </body>
    </html>

Si falla el acceso hola.html, hemos usado un directorio o una URL incorrectas. Si sólo falla hola.jsp, revisar el paso 'fijar variable JAVA_HOME'

1.1.3 Configurar el entorno de trabajo

Al arrancar el servidor se inicializa CLASSPATH para incluir las clases JSP y servlets estandards, y el directorio WEB-INF/classes de cada aplicación web (que contiene servlets compilados). Para crear nuestras propias aplicaciones debemos crear un entorno similar, siguiendo los siguientes pasos:

1.1.3.1 Crear un directorio de desarrollo

Creamos un directorio personal donde situar nuestros servlets y páginas JSP (ej.- en la unidad w:), sobre el que iremos creando subdirectorios para reflejar la estructura de nuestros paquetes. Es importante no usar directamente directorios de la instalación Tomcat

1.1.3.2 Fijar valor CLASSPATH

Servlets y JSP no forman parte de la edición estandard de la plataforma 2 de Java, de forma que el compilador (javac) puede generar mensajes de error al compilar servlets (clases desconocidas). Hay que fijar CLASSPATH para incluir common/lib/servlet.jar, nuestro directorio de desarrollo, y el directorio actual (.)

    set CLASSPATH=.;X;Y\common\lib\servlet.jar 

donde X es nuestro dir. de desarrollo e Y el directorio de instalación de Tomcat

1.1.3.3 Bookmark de los documentos de servlets y JSP

Abrimos en un navegador la página

    webapps/romcat-docs/servletapi/index.html

y la añadimos a favoritos (contiene la documentación sobre las clases javax.servlet). Con ello facilitamos el acceso a dicha ayuda

1.1.4 Compilar y probar servlets simples

Permite verificar que hemos realizado correctamente los pasos anteriores

1.1.4.1 Servlet que no usa paquetes, sólo salida java

Situamos este servlet (Hola.java) en nuestro directorio de desarrollo, y compilar (si hay errores, revisar el valor de CLASSPATH)

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class Hola extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
                "Transitional//EN\">\n";
            out.println(docType +
                "<HTML>\n" +
                "<HEAD><TITLE>Hola</TITLE></HEAD>\n" +
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H1>Hola</H1>\n" +
                "</BODY></HTML>");
        }
    }

Situar la clase Hola.class en el directorio ROOT/WEB-INF/classes (si el directorio no existe, lo creamos previamente)

Al acceder a http://localhost/servlet/Hola obtendremos una página HTML con el saludo.

1.1.4.2 Servlet que usa paquetes

Realizar los mismos pasos con el servlet Hola2.java. Al usar el paquete pd, debe incluirse en un subdirectorio PD tanto en el directorio de desarrollo como en el servidor (el fichero Hola2.class debe copiarse en ROOT/WEB-INF/classes/PD)

    package PD;

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class Hola2 extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                    throws ServletException, IOException {
            resp.setContentType("text/html");
            PrintWriter out = resp.getWriter();
            String docType =
                "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
                "Transitional//EN\">\n";
            out.println(docType +
                "<HTML>\n" +
                "<HEAD><TITLE>Hola (2)</TITLE></HEAD>\n" +
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H1>Hola (2)</H1>\n" +
                "</BODY></HTML>");
        }
    } 

Debemos acceder a la página con la URL http://localhost/servlet/PD.Hola2

1.1.4.3 Servlet que usa paquetes y clases auxiliares

Hola3.java es un servlet del paquete PD que utiliza la clase ServletAux.class definida en ServletAux.java

ServletAux.java

    package PD;

    import javax.servlet.*;
    import javax.servlet.http.*;

    public class ServletAux {
        public static final String DOCTYPE =
            "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
            "Transitional//EN\">";

        public static String headWithTitle(String title) {
            return(DOCTYPE + "\n<HTML>\n<HEAD><TITLE>" + title + "</TITLE></HEAD>\n");
        }

        // Lee el parametro indicado, y lo convierte a entero
        // si no existe, o formato ilegal, devuelve el valor por defecto
        public static int getIntParameter(HttpServletRequest request,
                            String nom, int porDefecto) {
            String s=request.getParameter(nom);
            int n;
            try {n=Integer.parseInt(s);} catch(NumberFormatException nfe) {n=porDefecto;}
            return n;
        }

        // Devuelve el cookie dado su nombre y
        // un vector de cookies (si no existe el nombre, devuelve null)
        public static Cookie getCookie(Cookie[] cookies, String nom) {
            if (cookies == null) return null;
            for(int i=0; i<cookies.length; i++) 
                if (nom.equals(cookies[i].getName())) return(cookies[i]);
            return(null);
        }

        // Devuelve el valor de un cookie dado su nombre y
        // un vector de cookies (si no existe el nombre, devuelve el valor por defecto
        public static String getCookieValue(Cookie[] cookies, String nom, String porDefecto) {
            Cookie c=getCookie(cookies,nom);
            if (c==null) return porDefecto; else return(c.getValue());
        }

        // Dada una tira, realiza las conversiones que
        // permiten insertarla en una página web
        // original     '<'     '>' '"'     '&'
        // sustituto    '&lt;'  '&gt;'  '&quot;'    '&amp;'

        public static String filter(String input) {
            StringBuffer filtered = new StringBuffer(input.length());
            char c;
            for(int i=0; i<input.length(); i++) {
                c = input.charAt(i);
                if (c == '<') filtered.append("&lt;");
                else if (c == '>') filtered.append("&gt;");
                else if (c == '"') filtered.append("&quot;");
                else if (c == '&') filtered.append("&amp;");
                else filtered.append(c);
            }
            return(filtered.toString());
        }
    }

hola3.java

    package PD;

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class Hola3 extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
              throws ServletException, IOException {
            resp.setContentType("text/html");
            PrintWriter out = resp.getWriter();
            String titulo = "Hola (3)";
            out.println(ServletAux.headWithTitle(titulo) + 
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H1>" + titulo + "</H1>\n" +
                "</BODY></HTML>");
        }
    }

Ambas clases (Hola3 y ServletAux) están en el paquete PD, y por tanto deben situarse en el directorio PD

1.1.5 Establecer método de 'carga'

Hasta ahora desarrollamos y compilamos en un entorno de desarrollo, y luego copiamos manualmente los ficheros .class al directorio correspondiente de Tomcat -> tedioso y propenso a errores. Existen distintas alternativas para automatizar el proceso.

Cada una es preferible a las anteriores, especialmente en desarrollos complejos

1.1.5.1 Copia de directorio usando enlaces simbólicos (acceso directo)

Es un método simple y adecuado para empezar, pero no para fase de producción (copia tanto los ficheros .class como los fuentes .java, y depende del servidor especifico)

Situados en X/ROOT/WEB-INF, pulsamos el botón derecho sobre la carpeta 'classes' y seleccionamos Copiar: vamos al directorio de desarrollo y seleccionamos 'pegar acceso directo'. Tras compilar, arrastramos los .class al icono de acceso directo.

La misma idea sirve para facilitar la descarga de otros tipos de fichero (HTML, JSP, etc.), creando en el directorio de desarrollo un icono de acceso directo a X/webapps/ROOT

1.1.5.2 Opción -d de javac

Por defecto, el compilador sitúa los .class en el mismo directorio donde están los fuentes, pero la opción -d permite indicar otro directorio destino. El único inconveniente es que sólo resuelve el problema de los ficheros java (los HTML, JSP, etc. hay que seguir gestionándolos manualmente)

Ej.- para compilar Hola2.java, escribimos javac -d

X/webapps/WEB-INF/classes Hola2.java

donde X es el directorio de instalación de Tomcat

1.1.5.3 Configurar editor

Se trata de configurar el entorno de desarrollo o compilador para que se encargue de realizar la descarga de los ficheros

1.1.5.4 Uso de la herramienta 'ant'

ant es una herramienta similar al make de Unix (desarrollada dentro del proyecto jakarta, el mismo que dió origen a Tomcat). Es muy flexible, pero requiere cierto periodo de aprendizaje (ver http://jakarta.apache.org/ant)

1.2 Ciclo de vida de los servlets

Para inicializar un servlet, la aplicación servidor carga la clase Servlet (y probablemente otras clases referenciadas) y crea una instancia invocando un constructor sin argumentos. Entonces invoca el método init del servidor (init(ServletConfig config))

En el método init debemos incluir toda inicialización previa al uso del servlet, y almacenar el objeto ServletConfig que posteriormente puede invocarse mediante el método getServletConfig(). Los servlets que extienden GenericServlets (o su subclase HttpServlet) deben invocar super.init(config) al principio del método init.

El objeto ServletConfig contiene parámetros del Servlet y una referencia al ServletContext del Servlet. Se garantiza que 'init' sólo se invoca una vez durante toda la vida del servlet. No necesita ser thread-safe, porque el método 'service' no se invocará hasta que termine init.

Una vez inicializado, el método 'service(ServletRequest req, ServletResponse resp)' cada vez que el servlet recibe una petición. El método se invoca de forma concurrente (varias tareas pueden invocar simultáneamente dicho método), y por tanto debe ser thread-safe (NOTA.- también existen técnicas para garantizar que se invoque de forma serie cuando resulte necesario).

Cuando necesita descargarse el servlet (ej.- porque debe cargarse en el servidor una nueva versión, o porque vamos a detener el servidor), debe invocarse el método destroy(). Cuando se invoca destroy todavía pueden quedar tareas ejecutando el método 'service', de forma que destroy debe ser thread-safe. Todo recurso reservado en init (ej.- conexiones de red, ficheros, accesos a BD) debe liberarse en destroy. Se garantiza que destroy se invoca una única vez durante toda la vida del servlet.

    init (service)* destroy

1.3 HTTP

Es un protocolo orientado a petición/respuesta.

Una petición HTTP consiste en:

Una respuesta contiene:

Un método HTTP llamado X conduce a invocar el método doX del servlet (ej.- GET supone invocar doGet). Todos estos métodos reciben los parámetros (HttpServletRequest req, HttpServletResponse resp):

Los métodos doOptions y doTrace proporcionan implementaciones por defecto, y normalmente no se sobreescriben. El método HEAD (que devuelve las mismas líneas de cabecera que GET, pero sin incluir el cuerpo) se realiza invocando doGet e ignorando la salida escrita por este método.

Nos quedan los métodos doGet, doPut, doPost y doDelete, cuyas implementaciones por defecto devuelven un error HTTP 'Bad Request'. Una subclase de HttpServlet sobreescribe uno o más de estos métodos, para proporcionar una implementación razonable.

Cuando solicitamos una URL en un navegador se usa el método GET. El método GET no incluye datos en el cuerpo. La respuesta debe contener un cuerpo con los datos de respuesta y campos de cabecera que describen el cuerpo (especialmente Content-Type y Content-Encoding). Cuando se envía un formulario HTML puede utilizarse tanto GET como POST. Con la petición GET los parámetros se codifican en la URL, mientras que en la petición POST los parámetros se incluyen en el cuerpo.

Los editores HTML y otras herramientas de desarrollo utilizan peticiones PUT para situar nuevos recursos en el servidor web, y peticiones DELETE para borrar recursos.

1.4 Generación HTML

Contenido estático (genera el mismo contenido cada vez que se invoca)

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class Hola extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            resp.setContentType("text/html");
            PrintWriter out = resp.getWriter();
            out.println("<HTML>\n" +
                "<HEAD><TITLE>Hola</TITLE></HEAD>\n" +
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H1>Hola</H1>\n" +
                "</BODY></HTML>");
            out.close();
        }
        public String getServletInfo() {
            return "Hola 1.0 Programación Distribuida y su Aplicación bajo Internet 2003"
        }
    }

Aspectos a comentar sobre el código:

En general, podemos considerar el siguiente esqueleto:

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class Hola extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            // usa req para leer la cabecera HTTP de la petición (ej.- cookies)
            // y datos del formulario HTML
            // usa resp para especificar la cabecera de la respuesta
            PrintWriter out = resp.getWriter();
            // usa out para enviar contenido al navegador
        }
    }
[ back to top ]

2 Formularios HTML

Cuando utilizamos servicios de compra por internet, buscadores, gestión de reservas, etc. utilizamos formularios para comunicar al servidor un conjunto de datos. Un formulario presenta un conjunto de etiquetas y campos que permiten al usuario la selección/introducción de datos. Podemos utilizar distintos modelos de formularios en la parte cliente:

  1. formulario PDF.- si el navegador dispone de un plug-in para visualizar directamente documentos PDF, podemos descargar un documento PDF que contenga un formulario
  2. formulario HTML.- forma parte del estandard HTML, y por lo tanto está soportado por todos los navegadores. Es el mecanismo utilizado con mayor frecuencia, y el único que vamos a desarrollar en esta asignatura.
  3. formulario construido mediante la ejecución de un programa en la parte cliente (normalmente un applet java). Aunque permite un mayor control sobre la interacción con el usuario (ej.- validación de datos sin necesidad de contactar con el servidor, etc.) introduce mayor complejidad y requiere cierto soporte en el navegador.

Aunque sólo vamos a considerar formularios HTML, los formularios PDF generan el mismo tipo de solicitud hacia el servidor (empaquetan igual los valores introducidos por el usuario), y por lo tanto son indistinguibles para un servlet.

El objetivo de este apartado es los sevlets a la creación de formularios HTML y a la gestión de la respuesta (obtención y procesamiento de los valores editados por el cliente).

2.1 Lectura de datos del formulario

Empezamos con un formulario HTML básico:

    <HTML>
    <BODY>
        <H1>Indica tus datos personales</H1>
        <FORM action="servlet/form0" method="GET">
        Nombre y apellidos:
            <INPUT type="text" name="nombre">
            <INPUT type="text" name="ap1">
            <INPUT type="text" name="ap2"> 
        <BR>
        Departamento: 
            <INPUT type="radio" checked name="depto"
            value="comercial">Comercial
            <INPUT type="radio" name="depto"
            value="desarrollo">Desarrollo
            <INPUT type="radio" name="depto"
            value="finanzas">Finanzas 
        <BR> <P>
        Indica el rasgo que define mejor tu personalidad:
            <SELECT name="personalidad"
            <OPTION value="responsable"</OPTION>Responsable
            <OPTION value="trabajador"</OPTION>Trabajador
            <OPTION value="ambicioso"</OPTION>Ambicioso
            <OPTION value="nervioso"</OPTION>Nervioso
            <OPTION value="educado"</OPTION>Educado
            <OPTION value="estudioso"</OPTION>Estudioso
            <OPTION value="valiente"</OPTION>Valiente
            </SELECT>
        <BR>
        <INPUT type="submit">
        </FORM> 
    </BODY>
    </HTML>

La marca 'FORM' del formulario HTML indica en su campo 'action' la aplicación que debe procesar la respuesta, y en su campo 'method' el método utilizado para acceder codificar los parámetros en la posterior petición al servidor (al pulsar el botón 'submit').

Cada campo del formulario posee un nombre único (propiedad 'name' de la marca correspondiente), y una vez cumplimentado por el usuario, un valor (un campo también puede tener un valor por defecto, indicado por la propiedad 'value').

La petición hacia el servidor contiene un conjunto de pares 'nombre=valor', donde 'nombre' corresponde al nombre del campo y 'valor' a su valor actual. El orden de dichos pares no importa, puesto que posteriormente el servidor accede al valor de cada campo por nombre, no por posición.

El conjunto de pares 'nombre=valor' puede hacerse llegar al servidor utilizando dos métodos distintos:

    'URL del programa indicado en action'?nombre1=valor1&nombre2=valor2&..'

La ventaja es la simplicidad (ej.- al ser una URL podemos guardar en 'favoritos' formularios con su contenido, o comprobar el funcionamiento del programa sin necesidad de rellenar el formulario). El inconveniente principal es la limitación de tamaño (la URL posee un tamaño máximo limitado, y por tanto limita la longitud del conjunto de pares 'clave=valor')

2.2 Gestión de GET y POST con un único servlet

Al definir un servlet como extensión de 'HttpServlet' podemos sobreescribir los métodos 'doGet' (se activa cuando la petición utiliza el método GET) y 'doPost' (se activa cuando la petición utiliza el método POST).

Si únicamente está previsto un método de invocación, es suficiente sobreescibir el correspondiente método 'doGet' o 'doPost', pero generalmente deseamos diseñar servlets capaces de responder a ambos tipos de petición, por lo que sobreescribimos ambos métodos.

El siguiente servlet sirve para gestionar el formulario 'form0.html' (en este ejemplo nos limitamos a recoger los valores, y confeccionar con ellos una página Html que se devuelve como resultado):

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class form0 extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            resp.setContentType("text/html");
            PrintWriter out = resp.getWriter();
            out.println("<HTML>\n" +
                "<HEAD><TITLE>Respuesta a form0.hml</TITLE></HEAD>\n" +
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H1>Respuesta al formulario form0.html</H1>\n" +
                "<UL>\n" +
                "  <LI>nombre: " +
                req.getParameter("nombre") + "\n" +
                "  <LI>ap1: " +
                req.getParameter("ap1") + "\n" +
                "  <LI>ap2: " +
                req.getParameter("ap2") + "\n" +
                "  <LI>depto: " +
                req.getParameter("depto") + "\n" +
                "  <LI>personalidad: " +
                req.getParameter("personalidad") + "\n" +
                "</UL>\n" +
                "</BODY></HTML>");
            out.close();
        }
        public void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            doGet(req,resp);
        }
        public String getServletInfo() {
            return "form0 1.0 Programación Distribuida y su Aplicación bajo Internet 2003";
        }
    }

El ejemplo anterior ilustra varios aspectos importantes:

Cuando un determinado campo posee múltiples valores (ej.- una lista con posibilidad de selección múltiple, varios campos de entrada con el mismo nombre, etc) el sufijo que se añade a la URL posee otros tantos pares 'nombre=valor'. Si asumimos un campo con nombre 'x' y valores 'valor1..valorN', tendremos:

    'x=valor1&x=valor2&...&x=valorN' 

El método 'getParameter' devuelve únicamente el primero de los valores; para acceder a todos ellos debemos utilizar 'getParameterValues', que devuelve un vector de Strings conteniendo los distintos valores.

El siguiente formulario (form1.html) contiene un campo con múltiples valores:

    <HTML>
    <BODY>
        <H1>Introduce una lista de nombres</H1>
        <FORM action="/servlet/form1" method="GET">
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>

            <INPUT type="submit">
        </FORM> 
    </BODY>
    </HTML>

Para procesar dicho formulario, utilizamos el siguiente servlet:

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class form1 extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            resp.setContentType("text/html");
            PrintWriter out = resp.getWriter();
            out.println("<HTML>\n" +
                "<HEAD><TITLE>Respuesta a form1.hml</TITLE></HEAD>\n" +
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H1>Respuesta al formulario form1.html</H1>\n");
            out.println("<H2>Parametros recibidos</H2>\n");
            String noms[]=req.getParameterValues("nombres");
            for (int i=0; i<noms.length; i++)
                out.println(noms[i]);
            out.println("</BODY></HTML>");
            out.close();
        }
        public void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            doGet(req,resp);
        }
        public String getServletInfo() {
            return "form0 1.0 Programación Distribuida y su Aplicación bajo Internet 2003";
        }
    }

Hasta el momento hemos supuesto que la aplicación conoce todos los nombres de los campos que aparecen en el formulario. Tambien es posible obtener de forma dinámica el conjunto de nombres (el conjunto de campos disponibles), utilizando el método 'getParameterNames'.

El resultado de dicho método es de tipo 'Enumeration' (concretamente java.util.Enumeration). La acción más frecuente sobre un tipo enumerado es visitar todos sus elementos (iterar), para lo cual utilizamos en un bucle los métodos 'hasMoreElements' y 'nextElement'.

El siguiente código ilustra el acceso a los campos de unformulario (localiza los distintos nombres y accede a su(s) valores)

    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.util.*;

    public class leeParam extends HttpServlet {
        public void service (HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
            resp.setContentType("text/html");
            PrintWriter out= resp.getWriter();
            out.println("<HTML>\n<BODY>\n Parametros recibidos \n<PRE>");
            // obtiene los valores del formulario
            Enumeration params= req.getParameterNames();

            while (params.hasMoreElements()) {
                String p= (String) params.nextElement();
                String v[]=req.getParameterValues(p);

                if (v.length==1) out.println(p+"="+v[0]);
                else {
                    out.print(p+"=");
                    for (int i=0; i<v.length; i++) {
                        if (i>0) out.print(",");
                        out.println(v[i]);
                    }
                    out.println();
                }
            }
            out.println("</PRE>\n</BODY>\n</HTML>");
        }
    }

En este ejemplo se ha recurrido a sobreescribir directamente el método 'service' en lugar de sobreescribir 'doGet' y 'doPost'. El efecto neto es el mismo que en ejemplos anteriores.

2.3 Validación de campos (sin contenido o contenido erróneo)

Tal y como se ha descrito en el apartado anterior, 'getParameter("X") puede generar resultados diferentes:

La respuesta ante cada una de las situaciones depende de la lógica deseada en la aplicación. Es habitual requerir que determinados campos tengan valor (campos requeridos), y en muchos casos es deseable que los valores de los campos cumplan determinadas propiedades (ej.- que el DNI introducido sea un valor numérico con el número de dígitos correcto).

Cuando el contenido de un formulario es incorrecto, lo deseable es volver a remitir el mismo formulario, pero manteniendo los valores correctos (como valores por defecto) y eliminando los valores incorrectos (además es deseable algún mensaje que indique porqué no eran correctos).

Esta labor debe realizarse en el servlet, utilizando los esquemas que se describen en el siguiente apartado.

2.4 Esquemas de gestión de formularios

Existen distintas posibilidades:

  1. El formulario HTML es estático, e indica (campo 'action') el servlet utilizado para procesarlo (es el esquema tradicional, utilizado en los ejemplos anteriores)
  2. El formulario HTML se crea de forma dinámica. Un servlet se encarga de crear el formulario, y otro de procesarlo
  3. Un único servlet se encarga de crear el formulario, y posteriormente procesarlo. Este esquema permite mantener estados intermedios del formulario (ej.- si un campo posee un valor incorrecto, podemos volver a remitir el formulario con los valores correctos en su lugar y una indicación del error, de forma que el usuario únicamente reescribe el campo erróneo)

2.4.1 Caso 1) formulario estático, procesado con servlet

Documento HTML estático (form2.html)

    <HTML>
    <HEAD>
        <TITLE>Cuestionario inicial</TITLE>
    </HEAD>
    <BODY>
        <H1>Cuestionario inicial</H1>
        <BR> <P> 
        Te agradeceria que dedicases unos minutos
        a rellenar este cuestionario 
        <BR>
        <FORM action="servlet/form2a" method="POST">
        1.- Indica tu lenguaje de programacion preferido
            <SELECT name="lengupref"
            <OPTION value="pascal"</OPTION>Pascal
            <OPTION value="c"</OPTION>C
            <OPTION value="c++"</OPTION>C++
            <OPTION value="delphi"</OPTION>Delphi
            <OPTION value="ada"</OPTION>Ada
            <OPTION value="java"</OPTION>Java
            <OPTION value="visual basic"</OPTION>Visual Basic
            <OPTION value="python"</OPTION>Python
            </SELECT>
        <P>
        2.- Indica tu Sistema Operativo preferido <BR>
            <INPUT type="radio" name="sopref" checked
            value="linux">Linux
            <INPUT type="radio" name="sopref"
            value="windows">Windows
            <INPUT type="radio" name="sopref"
            value="mac">MacOS
        <P>
        3.- Indica los lenguajes que has utilizado <BR>
            <INPUT type="checkbox" name="lengusa"
            value="pascal">Pascal
            <INPUT type="checkbox" name="lengusa"
            value="c">C
            <INPUT type="checkbox" name="lengusa"
            checked value="c++">C++
            <INPUT type="checkbox" name="lengusa"
            value="java">Java
            <INPUT type="checkbox" name="lengusa"
            value="ada">Ada
            <INPUT type="checkbox" name="lengusa"
            value="python">Python 
        <P>
        4.- Tecnologias que piensas utilizar en un futuro proximo <BR>
            <SELECT name="tecno" multiple size=3>
            <OPTION value="corba"</OPTION>Corba
            <OPTION value="rmi"</OPTION>RMI
            <OPTION value="ejb"</OPTION>EJB
            <OPTION value="com"</OPTION>COM
            <OPTION value="xml"</OPTION>XML
            <OPTION value="wap"</OPTION>WAP
            </SELECT>
        <P> <BR>
        <INPUT type="submit" value="envia">
        </FORM> 
    </BODY>
    </HTML>

Servlet que lo procesa (form2a.java)

    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.util.*;

    public class form2a extends HttpServlet {
        public void service (HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
            resp.setContentType("text/html");
            PrintWriter out= resp.getWriter();

            // obtiene los valores del formulario
            String lengupref  =req.getParameter("lengupref");
            String sopref     =req.getParameter("sopref");
            String[] lengusa  =req.getParameterValues("lengusa");
            String[] tecno    =req.getParameterValues("tecno");

            if (lengusa==null) lengusa= new String[0];
            if (tecno==null)  tecno = new String[0];

            // escribe los valores en un fichero
            try {
                PrintWriter fout= new PrintWriter(new FileWriter("datos",true));
                fout.println(lengupref); fout.println(sopref);
                for (int i=0; i<lengusa.length; i++) {
                    if (i>0) fout.print(", ");
                    fout.print(lengusa[i]);
                }
                for (int i=0; i<tecno.length; i++) {
                    if (i>0) fout.print(", ");
                    fout.print(tecno[i]);
                }
                fout.println(); fout.println("----------");
                fout.close();

                out.println("<HTML>\n<BODY>\n" +
                    "<H1>Gracias</H1>\n" +
                    "Gracias por participar en la encuesta\n" +
                    "</BODY>\n</HTML>\n");
            } catch (IOException e) {
                out.println("<HTML>\n<BODY>\n" +
                    "<H1>Error</H1>\n" +
                    "No se han podido grabar las respuestas" +
                    "debido a un error en el servidor\n" +
                    "</BODY>\n</HTML>\n");
            }
        }
    }

2.4.2 Caso 2) formulario creado con un servlet y procesado con otro

Para que el ejemplo tenga interés (y no se limite a generar de forma dinámica un formulario puramente estático), planteamos la generación dinámica de exámenes tipo test (ej.- con preguntas seleccionadas aleatoriamente de un gran conjunto inicial de preguntas).

El servlet form4.java se encarga de procesar los formularios remitidos por los alumnos. El objetivo es construir un histograma con la frecuencia de cada respuesta

NOTA.- En este ejemplo los datos no se añaden al final del fichero, sinoque se sobreescribe el fichero. Si lo que desamos no es sobreescibir, sino actualizar el fichero (ej.- para confeccionar un histograma), usamos la siguiente estrategia:

 creación se asume acceso concurrente y se reintenta posteriormente)
 modificamos (ej.- buscamos la respuesta para construir un histograma)
    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.util.*;

    public class form4a extends HttpServlet {
        public void service (HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
            resp.setContentType("text/html");
            PrintWriter out= resp.getWriter();
            String dir= req.getParameter("dir");
            try {
                File f=null;
                if (dir!=null) f= new File(dir,"form4.examen");
                else f=new File("form4.examen");

                BufferedReader in= new BufferedReader(new FileReader(f));
                String pregunta=in.readLine(), linea;
                Vector r= new Vector();
                // recoge las respuestas
                while ((linea=in.readLine()) != null) 
                    r.addElement(linea);
                in.close();

                // muestra la pregunta y las posibles respuestas
                out.println("<HTML>\n<BODY>");
                out.println("<FORM action=\"servlet/form4b\" method=\"POST\">");
                out.println("<B>pregunta</B> <BR>");
                Enumeration e= r.elements();
                while (e.hasMoreElements()) {
                    String s= (String) e.nextElement();

                    out.println("<INPUT type=\"radio\" name=\"respuesta\" value=\""+s+"\"> <"+s+"><BR>");
                }
                out.println("<P>\n<INPUT type=\"submit\" value=\"envia\">" +
                    "</FORM>\n</BODY></HTML>");

            } catch (IOException e) {
                out.println("<HTML>\n<BODY>\n" +
                    "<H1>Error</H1>\n" +
                    "El servidor no está disponible\n" +
                    "</BODY>\n</HTML>\n");
            }
        }
    }

y para procesarlo:

    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.util.*;

    public class form4b extends HttpServlet {
        public void service (HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
            resp.setContentType("text/html");
            PrintWriter out= resp.getWriter();
            String r= req.getParameter("respuesta");
            if (r==null) {
                out.println("<HTML>\n<BODY>\n" +
                    "<H1>No has contestado a la pregunta</H1>" +
                    "</BODY></HTML>");
                return;
            }
            try {
                String dir = System.getProperty("Form4b.dir");
                File f=null, f2=null;
                if (dir!=null) {
                    f= new File(dir,"form4b.a");
                    f2= new File(dir,"form4b.b");               
                } else {
                    f= new File("form4b.a");
                    f2= new File("form4b.b");               
                }
                boolean fin=false;
                for (int i=0; i<30; i++) {
                    if (f2.createNewFile()) {fin=true; break;}
                    try{Thread.sleep(1000);} catch (Exception e){}
                }
                if (!fin)
                    throw new IOException("no puedo reservar el fichero");

                Vector conteo=new Vector(), respuestas= new Vector();
                int nresp=0;
                try {
                    BufferedReader in= new BufferedReader(new FileReader(f));
                    PrintWriter out= new PrintWriter(new FileWriter(f2));
                    String linea;
                    boolean hayResp=false;

                while ((linea=in.readLine()) != null) {
                    // cada linea es de la forma "conteo:respuesta"
                    int pos= linea.indexOf(':');
                    if (pos<0) continue; // obviamos esa linea
                    String c,r;
                    c=linea.substring(0,pos); r=linea.substring(pos+1);
                    int contador=0;

                    try {contador=Integer.parseInt(c);} catch (Exception e) {}
                    if (r.equals(getParameter("resp")) {
                        hayResp=true; contador++;
                    }
                    nresp+=contador;
                    out.println(contador+":"+r);
                    respuestas.addElement(r);
                    conteo.addElement(new Integer(contador));
                }
                out.close(); in.close();
                f.delete(); f2.renameTo(f); // reemplaza fichero original

                // si la respuesta no aparece en el fichero mostramos el error
                // inicialmente deberia existir el fichero con todas las respuestas
                // y contadores a 0
                if (!hayResp) {
                    out.println("<HTML><BODY>" +
                        "<H1>Respuesta inesperada<H1>" +
                        "Tu respuesta no coincide con ninguna de las propuestas");
                    return;
                }
            } catch (IOException e) {
                try { f2.delete(); } catch (Exception e) {}
                out.println("<HTML>\n<BODY>\n" +
                    "<H1>Error</H1>\n" +
                    "El servidor no está disponible\n" +
                    "</BODY>\n</HTML>\n");
                return
            }
            out.println("<HTML>\n<BODY>\n" +
                "<H1>Gracias</H1>\n" +
                    "<H2>Gracias por tu colaboracion</H2> <P>" +
                   " <B>Estos son los resultados:</B> <P>" +
                   "</HTML></BODY>");
        }
    }

2.4.3 Caso 3) formulario creado y procesado con el mismo servlet

Esta estrategia tiene sentido cuando el cliente debe utilizar repetidamenteel mismo formulario, recordando en todo momento los contenidos anteriores(ej.- en una cesta de la compra se visualiza el estado actual, y se permiteañadir/borrar items repetidamente).

Inicialmente (la primera vez que se usa la página) debe generarse el formulario inicial, y posteriormente se presenta siempre el estado actual del formulario. Cuando se solicita un formulario debemos determinar si es o no la primera vez que se utiliza la página, para lo cual podemos usar distintas estrategias:

 se puede aplicar cuando queremos rellenar inicialmente campos con valores
 por defecto y pasamos esos valores como parte de la URL
    <INPUT type="hidden" name="yaEnviado" value=yes">

Cuando el usuario accede por vez primera dicha variable no es accesible

 (getParameter(yaEnviado) devuelve null)
 y el método gestor se declara en la marca <FORM> como POST (ej.- 

    <FORM action="servlet/prog" method="POST">

podemos distinguir entre la primera invocación y las siguientes averiguandoel método usado para transmitir los parámetros Ej.-

    if (request.getMethod().equals("POST")).. // no es la primera vez

Como ejemplo vamos a desarrollar un formulario que permita crear tarjetasde visita de forma iterativa (se rellenan los datos, se indica un formatode presentación, y el programa muestra el resultado; si no estamos satisfechos, volvemos a modificar el formulario, etc).El programa detectará también la ausencia de valor en campos requeridos,en cuyo caso mostrará una página con el mensaje correspondiente.

    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.util.*;

    public class leeParam extends HttpServlet {
        public void doPost (HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
            resp.setContentType("text/html");
            PrintWriter out= resp.getWriter();

            // obtiene los valores del formulario
            String nombre= request.getParameter("nombre");
            if (nombre==null) nombre="";
            String ap1= req.getParameter("ap1");   
            if (ap1==null) nombre="";
            String ap2= req.getParameter("ap2");
            if (ap2==null) nombre="";
            String direc= req.getParameter("direc");
            if (direc==null) nombre="";
            String telf= req.getParameter("telf");
            if (telf==null) nombre="";
            String email= req.getParameter("email");
            if (email==null) nombre="";
            String formato= req.getParameter("formato");
            if (formato==null) nombre="";

            // fija estilo por defecto para las etiquetas obligatorias
            String colorNombre="black", colorAp1="black", colorAp2="black", colorNotificacion="red";

            if (req.getMethod().equals("POST")) {
                boolean estaTodo=true;
                if (nombre.length==0) {colorNombre=colorNotificacion; estaTodo=false;}
                if (ap1.length==0) {colorAp1=colorNotificacion; estaTodo=false;}
                if (ap2.length==0) {colorAp2=colorNotificacion; estaTodo=false;}

                if (!estaTodo) {
                    out.println("<HTML>\n<BODY>\n" +
                        "<H1>Faltan valores requeridos</H1>\n" +
                        "Los campos a rellenar están marcados en " +
                        "<FONT color=\""+colorNotificacion+"\""+colorNotificacion+"> </FONT>" +
                           "</HTML></BODY>");
                } else { // muestra nombre y direccion
                    String nom= ap1+" "+ap2+" "+nombre+"<BR>"+dir+"<BR>"+email+" "+telf;
                    out.println("el nombre actual es: <P>");
                    if (formato.equals("negrita")) out.println("<B>"+nom+"</B>");
                    else if (formato.equals("negrita")) out.println("<I>"+nom+"</B>");
                    else out.println(nom);
                    out.println("<P>");
                }
            }

        out.println("<FORM action=\"tarjeta.jsp\" method="POST">" +
            "<TABLE>" +
            "<TR><TD>nombre: <TD> <INPUT type=\"text\" name=\"nombre\" value="+nombre+">" +
            "<TD><FONT color=\""+colorNombre+"\">requerido</FONT>" +
            "<TR><TD>nombre: <TD> <INPUT type=\"text\" name=\"ap1\" value=\""ap1+"\" +
            "<TD><FONT color=\""colorAp1"\">requerido</FONT>" +
            "<TR><TD>nombre: <TD> <INPUT type=\"text\" name=\"ap2\" value=\""+ap2+"\>" +
            "<TD><FONT color="\"+colorAp2+"\">requerido</FONT>" +
            "<TR><TD>nombre: <TD> <INPUT type=\"text\" name=\"direc\" value=\""+direc+"\>" +
            "<TR><TD>nombre: <TD> <INPUT type=\"text\" name=\"telf\" value=\""+telf+"\>" +
            "<TR><TD>nombre: <TD> <INPUT type=\"text\" name=\"email\" value=\""+email+"\>" +
            "<TR><TD>nombre: <TD> <INPUT type=\"text\" name=\"formato\" value=\""+formato+"\>" +
            "</TABLE>" +
            "<P>" +
            "Opciones de formato:" +
            "<SELECT name=\"formato\">" +
            "<OPTION value=\"normal\">Normal</OPTION>" +
            "<OPTION value=\"negrita\">Negrita</OPTION>" +
            "<OPTION value=\"italica\">Italica</OPTION>" +
            "</SELECT>" +
            "<P>" +
            "<INPUT type=\"submit\" value=\"Nueva tarjeta\">" +
            "</FORM>" +
            "</BODY>" +
            "</HTML>");
        }
    }
[ back to top ]

3 Gestión de cookies y sesiones

El protocolo HTTP no posee noción de estado (cada solicitud al servidor es independiente del conjunto de peticiones previas), pero en distintas aplicaciones resulta interesante introducir el concepto de estado en el servidor.

Podemos encontrar distintos escenarios en los que resulta importante la noción de estado:

3.1 Uso de cookies. Ventajas e inconvenientes

El uso de cookies no presenta riesgos de seguridad (únicamente se accede a un área del disco definida y controlada por el navegador), y facilita en gran medida el desarrollo del software servidor.

Desgraciadamente, su abuso puede comprometer el derecho de la intimidad (el cliente no es consciente del tráfico de información a/desde el servidor en relación con los cookies, de forma que pueden recogerse estadísticas, preferencias, etc. sin autorización del usuario).

De hecho, en la actualidad muchas de las aplicaciones que utilizan cookies 'agreden' en mayor o menor medida los derechos del cliente. Por esta razón, los cookies tienen mala prensa, y se tiende a deshabilitarlos (es una de las opciones de configuración del navegador).

La conclusión es que son una herramienta muy útil en entornos controlados (ej. una intranet), en los que los clientes 'confian' en el software de servidor desarrollado especificamente para dicha intranet.

3.2 Envío de cookies y lectura de cookies

El concepto de cookie es similar al de un diccionario (conjunto de pares <clave,valor>). La creación de nuevos cookies requiere tres pasos:

    Cookie c= new Cookie(nombre,valor);
    c.setMaxAge(80);
    resp.addCookie(c)

Además la clase Cookie define un conjunto de métodos:

    public void setName(String nom);
    public String getName();
    public void setValue(String val);
    public String getValue();
    public void setDomain(String dominio);
    public String getDomain();
    public void setMaxAge(int tiempoVida);
    public int getMaxAge();
    public void setPath(String path);
    public String getPath();
    public void setVersion(int version);
    public int getVersion();
    public void setSecure(boolean);
    public int getSecure();

Aunque setName y setValue no suelen utilizarse (normalmente especificamos el nombre del cookie y su valor en el constructor), getName y getValue resultan muy útiles para la lectura de cookies.

Normalmente, el navegador únicamente devuelve los cookies al servidor que los envión, pero podemos utilizar setDomain() para indicar al navegador que remita los cookies a otros hosts del mismo dominio. Ej.-

    c.setDomain(".upv.es")

para enviar el cookie c a todo host del Politécnico.

Podemos utilizar cookies de sesión (desaparecen al cerrar el navegador) o persistentes (sobreviven el tiempo de vida indicado, o hasta que el usuario los borra de forma explícita). setMaxAge permite especificar la caducidad en segundos. Un valor negativo (o ningún valor) indica que se trata de un cookie de sesión, 0 indica al navegador que borre el cookie, y un valor positivo indica tiempo de vida en segundos.

Por defecto, el navegador únicamente devolverá el cookie a URLs correspondientes al directorio donde está la aplicación que creó el cookie o alguno de sus subdirectorios. El método setPath permite indicar otras rutas a las que también se devolvería el cookie (setPath("/") significa enviar el cookie en todas las páginas).

Por defecto se crean cookies 'versión 0'. Se puede utilizar setVersion para cambiar a la versión 1, pero no lo soportan todos los navegadores (no se aconseja).

Por defecto, lo cookies se envían con indenpendencia de acceder o no a través de SSL. Con setSecure(true) indicamos que sólo se devuelvan los cookies al acceder a través de una conexión cifrada (SSL).

3.2.1 Lectura de cookies

Para leer cookies utilizamos el método getCookies definido en HttpServletRequest (o sea, aplicamos req.getCookies, siendo req uno de los parámetros al método service del servlet).

El método getCookies() devuelve un vector de objetos cookie (todos los cookies enviados por el navegador). Si no hay cookies se obtiene un vector de longitud 0.

Para acceder a cada cookie individual iteramos sobre el vector:

3.2.2 Conteo de acceso

Creamos un servlet simple que registra el número de 'visitas' al mismo. Deseamos mostrar el número de visitas para cada usuario, para lo cual creamos un cookie correspondiente al contador, e incrementamos el cookie en cada visita. El esqueleto de la aplicación queda como sigue:

  1. Obtener el vector de cookies
  2. Iterar sobre dicho vector, buscando el cookie 'contador'
  3. Si existe, incrementamos su valor en uno, y si no utilizamos 0 como valor inicial
  4. Añadimos el nuevo cookie al objeto respuesta
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class CookieCounter extends HttpServlet {

        public void doGet(HttpServletRequest req, HttpServletResponse res) 
            throws ServletException, IOException {
            String valor = null;
            int n=0; // contador, inicializado a su valor por defecto
            res.setContentType("text/html");

            Cookie[] cs = req.getCookies();
            if (cs!=null) 
                for (int i=0; i<cs.length; i++) {
                    if ((cs[i].getName()).equals("contador")) {
                        valor = cs[i].getValue(); 
                        break;
                    }
                }
            if (valor != null) n = Integer.parseInt (valor);
            n++;

            Cookie c = new Cookie ("contador", Integer.toString(n));
            c.setMaxAge (60*60*24*365); // caduca al cabo de un año
            res.addCookie (c);

            PrintWriter out = res.getWriter();
            out.println ("<HTML><BODY>");
            out.println ("<H1>Numero de visitas:  "+n);
            out.println ("</H1>");
            out.println ("</BODY></HTML>");
            out.close();
        }
    } 

3.3 Concepto de sesión

La alternativa/complemento al uso de cookies es la utilización se sesiones en el servidor. Los servlets incluyen un API java denominado Session y diseñado específicamente para este fin.

Con este mecanismo, podemos desarrollar fácilmente aplicaciones que dependen de datos específicos de un usuario (ej.- para servicios personalizados). Los pasos a seguir son:

  1. Obtener el objeto Session a partir del objeto petición (HttpRequest). Si el resultado es null, no existe un objeto sesión previo (primera visita de dicho usuario)
    HttpSession s=req.getSession(); // obtiene objeto sesion de ese usuario
    HttpSession s=req.getSession(true); //idem, pero crea automat. objeto sesión si no existe
    s.isNew(); // cierto si se trata de una nueva sesión

Cada usuario recibe de forma automática un identificador de sesión único (si el navegador soporta cookies, el servlet crea automáticamente un cookie con el identificador, y si no el servlet intenta extraer el identificador de la URL)

  1. Extraer información de dicho objeto sesión. El objeto sesión equivale a un diccionario que almacena pares clave/valor (en la parte valor podemos almacenar cualquier objeto).
    int n = (int)s.getValue(nombre); //devuelve valor como tipo Object -> requiere cast
    s.getValueNames(); //devuelve lista de claves (vector de strings)

Otros métodos útiles sobre el objeto sesión son:

    String getId(); //devuelve ID sesion
    boolean isNew(); // cierto si la sesión acaba de crearse
    long getCreationTime();
    long getLastAccessTime();
  1. Añadir datos a dicho objeto sesión. Utilizamos los siguientes métodos sobre el objeto sesión
    void putValue(String clave, Object valor); //añade par <clave,valor>
    void removeValue(String clave); //elimina par <clave,valor>
    void invalidate();  // invalida la sesión
    void encodeURL();   // añade ID sesión a la URL

El servidor invalida y borra una sesión cuando ha transcurrido un periodo determinado (dependiente del servidor) sin accesos desde ese usuario. La sesión puede invalidarse manualmente con 'invalidate'.

Si un navegador no soporta cookies se añade de forma automática el identificador de sesión a la URL. Para ello utilizamos el método encodeURL (la implementación concreta depende del servidor)

    String url = resp.encodeURL(urlOriginal);

Con este esquema 'toda' URL debe incluir el identificador de sesión (en instalaciones muy complejas resulta dificil ofrecer esa garantía).

3.3.1 Conteo de acceso

El siguiente ejemplo registra el número de visitas desde cada usuario.

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.net.*;
    import java.util.*;

    public class ShowSession extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        res.setContentType("text/html");
        PrintWriter out = res.getWriter();

        HttpSession sesion = req.getSession(true);
        Integer n = (Integer)sesion.getValue("contadorAcceso");
        String titular = n!=null?"Hola otra vez":"Hombre, un nuevo visitante!";
        int valor = n!=null?n.intValue()+1:0; // actualiza conteo
        sesion.putValue("contadorAcceso", new Integer(valor));

        out.println("<HTML>\n<HEAD>\n<TITLE>"+
            "Ejemplo conteo acceso usando sesiones"+
            "</TITLE>\n</HEAD>\n" +
            "<BODY BGCOLOR=\"#FDF5E6\">\n" +
            "<H1 ALIGN=\"CENTER\">" + titular   + "</H1>\n" +
            "<H2>Información sobre tu sesión:</H2>\n" +
            "<TABLE BORDER=1 ALIGN=\"CENTER\">\n" +
            "<TR BGCOLOR=\"#FFAD00\">\n" +
            "  <TH>Tipo información<TH>Valor\n" +
            "<TR>\n" +
            "  <TD>ID\n" +
            "  <TD>" + sesion.getId() + "\n" +
            "<TR><TD>Instante creación\n" +
            "<TD>"+new Date(sesion.getCreationTime()) + "\n" +
            "<TR><TD>Ultimo acceso\n" +
            "  <TD>"+new Date(sesion.getLastAccessedTime()) + "\n" +
            "<TR>\n" +
            "  <TD>Número de acceso previos\n" +
            "  <TD>" + valor + "\n" +
            "</TR>"+
            "</TABLE>\n</BODY></HTML>");
        }

        public void doPost(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {doGet(req, res);}
    }
[ back to top ]

4 JSP

JSP (Java Server Pages) es un mecanismo que permite incorporar código java dentro de páginas HTML. El objetivo básico es separar la presentación (HTML) del contenido dinámico (generado al ejecutar el código Java). Sin embargo, también podemos utilizar JSP para sustituir servlets en cualquier otra función (en definitiva un JSP es una abstracción de un servlet, excepto que trabajamos a mayor nivel).

Para ilustrar la utilización de JSP, los utilizaremos para procesar el mismo conjunto de formularios que en apartados previos gestionábamos utilizando servlets.

El siguiente fichero se denomina form0.html, y difiere del presentado al discutir servlets en el valor de la propiedad 'action' (el lugar de servlet/form0 utilizamos form0.jsp). La justificación es:

    <HTML>
    <BODY>
        <H1>Indica tus datos personales</H1>
        <FORM action="form0.jsp" method="GET">
        Nombre y apellidos:
            <INPUT type="text" name="nombre">
            <INPUT type="text" name="ap1">
            <INPUT type="text" name="ap2"> 
        <BR>
        Departamento: 
            <INPUT type="radio" checked name="depto"
            value="comercial">Comercial
            <INPUT type="radio" name="depto"
            value="desarrollo">Desarrollo
            <INPUT type="radio" name="depto"
            value="finanzas">Finanzas 
        <BR> <P>
        Indica el rasgo que define mejor tu personalidad:
            <SELECT name="personalidad"
            <OPTION value="responsable"</OPTION>Responsable
            <OPTION value="trabajador"</OPTION>Trabajador
            <OPTION value="ambicioso"</OPTION>Ambicioso
            <OPTION value="nervioso"</OPTION>Nervioso
            <OPTION value="educado"</OPTION>Educado
            <OPTION value="estudioso"</OPTION>Estudioso
            <OPTION value="valiente"</OPTION>Valiente
            </SELECT>
        <BR>
        <INPUT type="submit">
        </FORM> 
    </BODY>
    </HTML>

El código que procesa el formulario anterior corresponde al fichero form0.jsp

    <HTML>
    <BODY>
        <%
            // obtiene los valores del formulario
            String nom=request.getParameter("nombre");
            String ap1=request.getParameter("ap1");
            String ap2=request.getParameter("ap2");
            String depto=request.getParameter("depto");
            String personalidad=request.getParameter("personalidad");
        %>
        <H1>Hola, <%=nom%> <%=ap1%> <%=ap2%>!</H1>   
        Veo que a pesar de trabajar en el departamento <%=depto%> tienes un caracter
        <%=personalidad%>
    </BODY>
    </HTML>

El anterior ejemplo ilustra la utilización de las marcas (<% ... %> y <%= ... %>) para delimitar los fragmentos de código. El resto del texto es HTML puro.

4.1 Bloques y Expresiones

En los bloques de código (entre <% y %>) se puede incluir cualquier conjunto de sentencias java válidas, incluyendo comentarios, declaraciones, y en general cualquier fragmento de código java.

Las marcas <%= ... %> permiten indicar una expresión (literal, variable, o combinación de literales y/o variables utilizando operadores y/o llamadas a métodos). Al evaluar una expresión se genera un resultado; el sistema transforma dicho resultado en una tira de caracteres, y escribe dicha cadena en lugar de <% ... %>. Este mecanismo es el que permite incorporar contenido dinámico (calculado al solicitar el documento).

4.2 Variables predefinidas

Cada JSP puede acceder a un conjunto de objetos predefinidos, siendo los más importantes el objeto 'request' (con información sobre la petición desde el navegador) y el objeto 'out' (flujo de salida para devolver datos al navegador).El objeto 'request' se utiliza principalmente para obtener los valores de las variables del formulario, mediante los métodos 'getParameter(String nombre)', 'getParameterValues(nom)' y 'getParameterNames()'. Tambien sirve para acceder a cualquier otra información asociada a la solicitud (ej. getRemoteHost).

El objeto 'out' puede utilizarse para devolver datos de forma directa (desde un bloque de código), aunque normalmente se recurre a devolver datos al navegador utilizando expresiones (<%= ... %>).

Existe un objeto 'response', que modifica la forma en que se devuelve la respuesta (por ejemplo, response.setRedirect(url)' indica al navegador que debe cargar una URL distinta).

El método 'getParameter(nom)' devuelve únicamente el primer valor asociado a dicho nombre. Dado que resulta posible que un mismo nombre tenga varios valores asociados, para acceder a todos ellos debemos utilizar 'getParameterValues', que devuelve un vector de Strings conteniendo los distintos valores

El siguiente formulario (form1.html) contiene un campo con múltiples valores:

    <HTML>
    <BODY>
        <H1>Introduce una lista de nombres</H1>
        <FORM action="form1.jsp" method="GET">
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>
            <INPUT type="text" name="nombres"> <BR>

            <INPUT type="submit">
        </FORM> 
    </BODY>
    </HTML>

Procesamos dicho formulario mediante el fichero form1.jsp:

    <HTML>
    <BODY>
        Parametros recibidos
        <PRE>
        <%
            // obtiene los valores del formulario
            String noms[]=request.getParameterValues("nombres");
            for (int i=0; i<noms.length; i++) {
            out.println(noms[i]);
        %>
        </PRE>
    </BODY>
    </HTML>

Cuando deseamos obtener de forma dinámica el conjunto de nombres (conjunto de campos disponibles) utilizando 'getParameterNames()'. El resultado es de tipo Enumeration, sobre el cual podemos iterar mediante un bucle (utilizando los métodos 'hasMoreElements' y 'nextElement').

    <HTML>
    <BODY>
        Parametros recibidos
        <PRE>
        <%
            // obtiene los valores del formulario
            java.util.Enumeration params= request.getParameterNames();

            while (params.hasMoreElements()) {
                String p= (String) params.nextElement();
                String v[]=request.getParameterValues(p);

                if (v.length==1) out.println(p+"="+v[0]);
                else {
                    out.print(p+"=");
                    for (int i=0; i<v.length; i++) {
                        if (i>0) out.print(",");
                        out.println(v[i]);
                    }
                    out.println();
                }
            }
        %>
        </PRE>
    </BODY>
    </HTML>

4.3 Directivas y declaraciones

La sintaxis <%@ ... %> permite incluir directivas JSP. Se trata de indicaciones necesarias para interpretar correctamente el fichero, pero no corresponden directamente a bloques de código ni expresiones. Podemos distinguir tres usos principales:

    <%@ page import="java.util.*, java.text.*" %>
    <%@ include file="otroFichero.jsp" %>
    <jsp:forward page="Prueba.jsp"/>

El código java incluido en un JSP corresponde a código dentro de un único método de la clase final (cuando el sistema transforma el JSP en un servlet, el código java corresponde al contenido del método 'service' del servlet). Cualquier declaración de variable corresponde a una variable local dentro de dicho método.

Si deseamos declarar otros métodos (ej. métodos auxiliares) u otros campos de la clase, debemos utilizar un bloque de declaraciones, definido entre las marcas <%! ... %>.

NOTA.- aunque la declaración de métodos auxiliares puede simplificar el código, la declaración de nuevos campos entraña ciertos riesgos; cada petición a un servlet supone una nueva actividad (thread), y el acceso a objetos compartidos puede dar lugar a interferencias. Tenemos dos opciones:

  1. Utilizar la variable session, donde podemos definir nuevos pares clave/valor de forma segura, en lugar de declarar variables compartidas
  2. Aplicar estrategias de programación concurrente (exclusión mútua y sincronización condicional) para prevenir interferencias

4.4 Procesamiento de formularios

El procesamiento de formularios utilizando JSP es similar al desarrollado utilizando servlets. A continuación se describen distintos ejemplos:

Procesamos el formulario form2.html:

    <HTML>
    <HEAD>
        <TITLE>Cuestionario inicial</TITLE>
    </HEAD>
    <BODY>
        <H1>Cuestionario inicial</H1>
        <BR> <P> 
        Te agradeceria que dedicases unos minutos a rellenar este cuestionario <BR>
        <FORM action="form2.jsp" method="POST">
            1.- Indica tu lenguaje de programacion preferido
                <SELECT name="lengupref"
                <OPTION value="pascal"</OPTION>Pascal
                <OPTION value="c"</OPTION>C
                <OPTION value="c++"</OPTION>C++
                <OPTION value="delphi"</OPTION>Delphi
                <OPTION value="ada"</OPTION>Ada
                <OPTION value="java"</OPTION>Java
                <OPTION value="visual basic"</OPTION>Visual Basic
                <OPTION value="python"</OPTION>Python
                </SELECT>
                <P>
            2.- Indica tu Sistema Operativo preferido <BR>
                <INPUT type="radio" name="sopref" checked value="linux">Linux
                <INPUT type="radio" name="sopref"         value="windows">Windows
                <INPUT type="radio" name="sopref"         value="mac">MacOS
                <P>
            3.- Indica los lenguajes que has utilizado <BR>
                <INPUT type="checkbox" name="lengusa" value="pascal">Pascal
                <INPUT type="checkbox" name="lengusa" value="c">C
                <INPUT type="checkbox" name="lengusa" checked value="c++">C++
                <INPUT type="checkbox" name="lengusa" value="java">Java
                <INPUT type="checkbox" name="lengusa" value="ada">Ada
                <INPUT type="checkbox" name="lengusa" value="python">Python 
                <P>
            4.- Tecnologias que piensas utilizar en un futuro proximo <BR>
                <SELECT name="tecno" multiple size=3>
                <OPTION value="corba"</OPTION>Corba
                <OPTION value="rmi"</OPTION>RMI
                <OPTION value="ejb"</OPTION>EJB
                <OPTION value="com"</OPTION>COM
                <OPTION value="xml"</OPTION>XML
                <OPTION value="wap"</OPTION>WAP
                </SELECT>
                <P> <BR>
                <INPUT type="submit" value="envia">
        </FORM> 
    </BODY>
    </HTML>

utilizando el JSP definido en el fichero form2.jsp

    <%@ page language="java" import="java.io.*;" %>
    <HTML>
    <BODY>
        <%
            // obtiene los valores del formulario
            String lengupref  =request.getParameter("lengupref");
            String sopref     =request.getParameter("sopref");
            String[] lengusa  =request.getParameterValues("lengusa");
            String[] tecno    =request.getParameterValues("tecno");


        if (lengusa==null) lengusa= new String[0];
            if (tecno==null)  tecno = new String[0];

            // escribe los valores en un fichero
            try {
                PrintWriter fout= new PrintWriter(new FileWriter("\\tmp\\datos",true));
                fout.println(lengupref); fout.println(sopref);
                for (int i=0; i<lengusa.length; i++) {
                    if (i>0) fout.print(", ");
                    fout.print(lengusa[i]);
                }
                for (int i=0; i<tecno.length; i++) {
                    if (i>0) fout.print(", ");
                    fout.print(tecno[i]);
                }
                fout.println(); fout.println("----------");
                fout.close();
                %> 
                <H1>Gracias</H1>
                Gracias por participar en nuestra encuesta
                <%
            } catch (IOException e) {
                application.log("error al grabar resultados",e);
                %>
                <H1>Error</H1>   
                No se han podido grabar las respuestas debido a un error en el servidor
                <% 
            }
        %>
    </BODY>
    </HTML>

4.4.1 Formulario JSP, gestor JSP

Asumimos la generación dinámica de exámenes tipo test, obteniendo los datos (preguntas y posibles respuestas) desde un fichero de texto.

    <%@ page language="java" import="java.io.*;" %>
    <%@ page language="java" import="java.util.*;" %>
    <HTML>
    <BODY>
    <%
        try {
            String dir= System.getProperty("form4.dir");
            File f=null;

            if (dir!=null) f= new File(dir,"form4.in");
            else f=new File("form4.in");

            BufferedReader in= new BufferedReader(new FileReader(f));
            String pregunta=in.readLine(), linea;
            Vector resp= new Vector();
                // recoge las respuestas
            while ((linea=in.readLine()) != null) 
                resp.addElement(linea);
            in.close();

            // muestra la pregunta y las posibles respuestas
    %>
            <FORM action="form4b.jsp" method="POST">
            <B> <%= pregunta%> </B> <BR>
        <% 
            Enumeration e= resp.elements();
            while (e.hasMoreElements()) {
                String s= (String) e.nextElement();
        %>
            <INPUT type="radio" name="respuesta" value="<%=s%>"> <%=s%>
            <BR>
        <%
            }
        >%
            <P>
            <INPUT type="submit" value="envia">
            </FORM>
        <%
        } catch (Exception e) {
        %>
        <H1>Error</H1>
            El servidor no esta disponible en este momento
        <%
        }
        %>
    </BODY>
    </HTML>

y para procesarlo:

    <%@ page language="java" import="java.io.*;" %>
    <%@ page language="java" import="java.util.*;" %>
    <HTML>
    <BODY>
        <%
        String resp= request.getParameter("respuesta");
        if (resp==null) {
            %>
            <H1>OJO</H1>
                No has contestado a la pregunta
            <%
            return;
        }
        try {
            String dir= System.getProperty("form4.dir");
            File f=nul, f2=null;
            if (dir != null) {
                f=new File(dir,"form4b.dat");
                f2=new File(dir,"form4b.tmp");
            } else {
                f=new File("form4b.dat");
                f2=new File("form4b.tmp");
            }

            boolean fin=false;
            for (int i=0; i<30; i++) {
                if (f2.createNewFile()) {fin=true; break;}
                try{ Thread.sleep(1000); } catch (Exception e) {}
            }
            if (!fin) 
                throws new IOException("no puedo reservar el fichero");

            Vector conteo=new Vector, respuestas= new Vector();
            int nresp=0;
            try {
                BufferedReader in= new BufferedReader(new FileReader(f));
                PrintWriter out= new PrintWriter(new FileWriter(f2));
                String linea;
                boolean hayResp=false;

                while ((linea=in.readLine()) != null) {
                    // cada linea es de la forma "conteo:respuesta"
                    int pos= linea.indexOf(':');
                    if (pos<0) continue; // obviamos esa linea
                    String c,r;
                    c=linea.substring(0,pos); r=linea.substring(pos+1);
                    int contador=0;

                    try {
                        contador=Integer.parseInt(c);
                    } catch (Exception e) {}
                    if (r.equals(getParameter("resp")) {
                        hayResp=true; contador++;
                    }
                    nresp+=contador;
                    out.println(contador+":"+r);
                    respuestas.addElement(r);
                    conteo.addElement(new Integer(contador));
                }
                out.close(); in.close();
                f.delete(); f2.renameTo(f); // reemplaza fichero original

                // si la respuesta no aparece en el fichero mostramos el error
                // inicialmente deberia existir el fichero con todas las respuestas
                // y contadores a 0
                if (!hayResp) {
                    %>
                    <H1>Respuesta inesperada<H1>
                    Tu respuesta no coincide con ninguna de las propuestas
                    <%
                    return;
                }
            } catch (IOException e) {
                try { f2.delete(); } catch (Exception e) {}
                %>
                <H1>Error</H1>
                El servidor no esta disponible en este momento
                <%
                return;
            }
        %>
        <H2>Gracias por tu colaboracion</H2> <P>
        <B>Estos son los resultados:</B> <P>
    </BODY>
    </HTML>

4.4.2 Un único JSP para generar y gestionar el formulario

Inicialmente (la primera vez que se usa la página) debe generarse el formulario inicial, y posteriormente se presenta siempre el estado actual del formulario. Cuando se solicita un formulario debemos determinar si es o no la primera vez que se utiliza la página

    <HTML>
    <BODY>
        <% 
        String nombre= request.getParameter("nombre");
        String ap1= request.getParameter("ap1");   
        String ap2= request.getParameter("ap2");
        String direc= request.getParameter("direc");
        String telf= request.getParameter("telf");
        String email= request.getParameter("email");
        String formato= request.getParameter("formato");

        if (nombre==null) nombre="";
        if (ap1==null) nombre="";
        if (ap2==null) ap2="";
        if (direc==null) direc="";
        if (telf==null) telf="";
        if (email==null) email="";
        if (formato==null) formato="";

        // fija estilo por defecto para las etiquetas obligatorias
        String colorNombre="black", colorAp1="black", colorAp2="black", colorNotificacion="red";

        if (request.getMethod().equals("POST")) {
            boolean estaTodo=true;
            if (nombre.length==0) {colorNombre=colorNotificacion; estaTodo=false;}
            if (ap1.length==0) {colorAp1=colorNotificacion; estaTodo=false;}
            if (ap2.length==0) {colorAp2=colorNotificacion; estaTodo=false;}

            if (!estaTodo) {
                %>
                Faltan valores en campos requeridos. Los campos a rellenar están marcados en 
                <FONT color=<%= colorNotificacion%>> <%= colorNotificacion%> </FONT>
                <%
            } else { // muestra nombre y direccion
                String nom= ap1+" "+ap2+" "+nombre+"<BR>"+dir+"<BR>"+email+" "+telf;
                out.println("el nombre actual es: <P>");
                if (formato.equals("negrita")) out.println("<B>"+nom+"</B>");
                else if (formato.equals("negrita")) out.println("<I>"+nom+"</B>");
                else out.println(nom);
                out.println("<P>");
            }
        }
        %>
        <FORM action="tarjeta.jsp" method="POST">
            <TABLE>
            <TR><TD>Nombre: <TD> <INPUT type="text" name="nombre" value=<%= nombre%>
            <TD><FONT color="<%= colorNombre%>">requerido</FONT>
            <TR><TD>Primer apellido: <TD> <INPUT type="text" name="ap1" value=<%= ap1%>
            <TD><FONT color="<%= colorAp1%>">requerido</FONT>
            <TR><TD>Segundo apellido: <TD> <INPUT type="text" name="ap2" value=<%= ap2%>
            <TD><FONT color="<%= colorAp2%>">requerido</FONT>
            <TR><TD>Dirección: <TD> <INPUT type="text" name="direc" value=<%= direc%>
            <TR><TD>Teléfono: <TD> <INPUT type="text" name="telf" value=<%= telf%>
            <TR><TD>Correo electrónico: <TD> <INPUT type="text" name="email" value=<%= email%>
            <TR><TD>Formato: <TD> <INPUT type="text" name="formato" value=<%= formato%>
            </TABLE>

            <P>
            Opciones de formato:
            <SELECT name="formato">
            <OPTION value="normal">Normal</OPTION>
            <OPTION value="negrita">Negrita</OPTION>
            <OPTION value="italica">Italica</OPTION>
            </SELECT>
            <P>
            <INPUT type="submit" value="Nueva tarjeta">
        </FORM>       
    </BODY>
    </HTML>
[ back to top ]

5 Acceso a base de datos con JDBC

5.1 Conceptos básicos

Recordamos los conceptos fundamentales desarrollados en la teoría:

5.2 Registro de drivers JDBC

La clase 'DriverManager':

Las clases 'Driver' poseen un inicializador static que crea una instancia e invoca DriverManager.registerDriver()

    jdbc.drivers=sun.jdbc.odbc.JdbcOdbcDriver;

5.3 URLs para JDBC

    jdbc:odbc:mibd
    jdbc:dcenaming:nominas
    jdbc:netdb//jsendra:1088/art
    jdbc:odbc:mibd;UID=js;PWD=patata

5.4 Uso de la conexión

5.4.1 Sentencias

5.5 Interrogación básica

    Class.forName('sun.jdbc.odbc.JdbcOdbcDriver');
    Conection conn = DriverManager.getConnection('jdbc:odbc:mibd','','');
    Statement sent = conn.createStatement();
    ResultSet rs = sent.executeQuery(
        "SELECT AP1, NOM, TOTAL" +
        "FROM estudiantes" +
        "ORDER BY TOTAL DESC");
    while (rs.next()) {
        String s= rs.getString('NOM') + " " +
            rs.getString("AP1") + ": " +
            rs.getString("TOTAL");
        System.out.println(s);
    }

5.6 Transacciones

5.7 Pasos para la puesta en marcha

5.8 Ejemplos

Los siguientes ejemplos asumen una configuración concreta:

En consecuencia, la conexión se consigue con las sentencias

    Class.forName("k.jdbc");
    Connection c = DriverManager.getConnection("jdbc:kx://dir:2003/mibd");

siendo 'dir' la dirección de red de la máquina servidor (se indicará en el propio laboratorio) y mibd la BD a la que se desea acceder. Para probar estos ejemplos en otros entornos sería necesario modificar dichas líneas.

    Class.forName('sun.jdbc.odbc.JdbcOdbcDriver');
    Conection c = DriverManager.getConnection('jdbc:odbc:mibd','','');

El primer ejemplo sirve como comprobación del funcionamiento, y no requiere una Base de Datos previa (el propio código crea una tabla y realiza posteriormente una interrogación)

    import java.sql.*;

    public class test{
        public static void main(String args[]){
            try{
                Class.forName("k.jdbc");  //carga el driver
                Connection c = DriverManager.getConnection("jdbc:kx://dir:2003");

                Statement e = c.createStatement();
                e.execute("create table t(i int,f float,s varchar,d date,t time,z timestamp,b varbinary)");

                PreparedStatement p = c.prepareStatement("insert into t values(?,?,?,?,?,?,?)");
                p.setInt(1,2);
                p.setDouble(2,2.3);
                p.setString(3,"asd");
                p.setDate(4,Date.valueOf("2000-01-01"));
                p.setTime(5,Time.valueOf("12:34:56"));
                p.setTimestamp(6,Timestamp.valueOf("2000-01-01 12:34:56"));
                byte[] b=new byte[2];
                b[0]=0x61;b[1]=0x62;
                p.setBytes(7,b);
                p.execute();

                ResultSet r = e.executeQuery("select * from t");
                ResultSetMetaData m= r.getMetaData();
                int n=m.getColumnCount();

                for(int i=0;i<n;)
                    System.out.println(m.getColumnName(++i));
                while(r.next())
                    for(int i=0;i<n;)
                        System.out.println(r.getObject(++i));

                c.close();
            }
            catch(Exception e){System.out.println(e.getMessage());}
        }
    }

Para el segundo ejemplo suponemos almacenada en el servidor la BD 'tutor', desarrollada en el correspondiente tema de teoría. Podemos ilustrar la interrogación mediante el siguiente ejemplo:

    import java.sql.*;
    public class EjTutor {
        public static void main(String[] arg) {
            try {
                Class.forName("k.jdbc");
                Connection c = DriverManager.getConnection("jdbc:kx://dir:2003/tutor.t");
                Statement sent = c.createStatement();
                ResultSet rs = 
                    sent.executeQuery("$select numMat, nota from notas");

                while (rs.next()) {
                    String s= rs.getString("numMat") + " " +
                        rs.getString("nota");
                    System.out.println(s);
                }
                sent.close();
            }
            catch (Exception e) {e.printStackTrace();}
        }
    }

Puedes probar otras interrogaciones. A continuación se indican algunas de las posibilidades:

    $select nombre from alumno where numMat=3567
    $select avg(nota) from notas where asig='Calculo'
    $select count(numMat) from notas 
        where (asig='Calculo' and nota>=5) or (asig='Diseño' and nota>=5)
    $select numMat from notas where asig='Algebra' and nota<5
    $select numTelf from hab where numHab='120D'
    $select alumno.nombre from alumno, notas 
        where (alumno.numMat=notas.numMat) 
        and (notas.asig='Algebra' and notas.nota<5)
    $insert into notas (numMat, asig, conv, nota) 
        values (4356,'Fisica','dic01',5.6)
    $select alumno.nombre from alumno, notas 
        where alumno.numMat=notas.numMat 
        and notas.asig='Calculo' 
        and (notas.nota > (select avg(nota) from notas where asig='Calculo'))

El servidor también dispone de las bases de datos "piezas" y "cafe", utilizadas como ejemplos en la parte de teoría. Pueden probarse interrogaciones/actualizaciones sobre dichas BD.

[ back to top ]