Demo sistema reclamos Ajax + Servlet + JSP en NetBeans 6.1

sábado, 17 de mayo de 2008
Demo sistema de reclamos Ajax – Servlet - JSP

Contenido

1 Introducción
2 Overview
3 Requerimientos
4 Manos a la obra
4.1 index.jsp
4.2 misFunciones.js
4.2.1 Función enviarDatosAjaxServlet
4.2.2 Función procesarRespuesta
4.2.3 Función valorXml
4.2.4 Función cargarContenidoDinamico
4.3 Servlet MiServlet.java
5 resultado.jsp
6 Conclusiones
7 Anexos


1 Introducción


Durante el desarrollo de un sistema web para mi tesis tuve algunos problemas asociados a Ajax y Java.

El problema general era que no sabía como comunicar dos tecnologías distintas, obtener datos con ajax y enviarlos al backend de la aplicación y desde ésta enviar una respuesta a ajax y desplegar lo que corresponda.

En Internet abundan los ejemplos, que hay que utilizar miAjax.responseText, miAjax.responseXML, etc. pero ningún ejemplo concreto y funcional, todo queda en teoría y poco de práctica.

Tampoco se habla mucho del tema de la codificación de datos, una cosa es enviar datos así como Dios los trajo al mundo y otra distinta es enviarlos y recibirlos tal cual, hablo específicamente de los caracteres especiales (ñ, tíldes, etc.), caracteres que son parte de nuestro lenguaje español (y que al parecer los gringos omiten en sus ejemplos, lo cual trae un par de dolores de cabeza a los de habla hispana).

Bueno, la verdad es que después de mucho averiguar, leer, probar y tomar un par de pastillas para el dolor de cabeza he logrado hacerlo y debo decir que es muy fácil, pero hay varios detalles que se deben tener en cuenta.

2 Overview

El ejemplo que veremos se trata de un demo de un sistema de reclamos. La idea es ingresar el nombre de la persona que reclama, el detalle del reclamo y la fecha de ingreso del reclamo.

Los primeros dos datos se harán simplemente utilizando un input y un textarea de html, mientras que para la fecha se utilizará un widget ajax.

Estos datos se enviarán a un servlet que los validará, de estar correctos los almacenará en una sesión y retornará una respuesta a la función ajax.

Una vez que se tenga la respuesta se procederá a realizar la acción correspondiente.

3 Requerimientos

  • NetBeans 6.1
  • Jmaki 1.7.3 (plugin de NetBeans 6.1)

NetBeans 6.1 se puede descargar gratis desde el sitio oficial en http://www.netbeans.org para Windows o Linux. Requiere instalado previamente el kit de desarrollo de java JDK, para la realización de este tutorial se utilizó la versión 1.6.0 update 6 y se puede descargar desde http://java.sun.com/javase/downloads/?intcmp=1281

Jmaki se puede descargar desde el administrador de plugines de NetBeans.

También se utilizó el servidor de aplicaciones GlassFish V2 UR2 que viene incluido en NetBeans 6.1.

4 Manos a la obra

Lo primero es crear un proyecto web en NetBeans 6.1, para esto se pincha el botón new project → Web → Web Application, le damos por nombre AjaxServlet u otro, presionamos next y luego cargamos el módulo Jmaki.

Debemos crear los siguientes recursos en el proyecto.

  • index.jsp (viene por default).
  • resultado.jsp
  • MiServlet.java
  • misFunciones.js


4.1 index.jsp

Una vez listo aparecerá una página index con un par de cosas. Eliminamos todo lo que esta entre los tags y agregamos lo siguiente:

<h1>Ingresar reclamo</h1>

<div id="error" style="color: red;"></div>

<form onsubmit="enviarDatosAjaxServlet(); return false;" >

<table>

<tr><td><b>* Nombre:</b></td><td><input id="nombre" name="nombre" size="20" maxlength="20" ><a:widget name="yahoo.tooltip" args="{context:['nombre']}" value="Ingrese su nombre aquí." /></input></td></tr>

<tr><td><b>* Comentario:</b></td><td><a:widget name="yahoo.tooltip" args="{context:['comentario']}" value="Ingrese el detalle del reclamo aquí." /><textarea id="comentario" name="comentario" rows="4" cols="25"></textarea></td></tr>

<tr><td><b>* Fecha:</b></td><td><a:widget id="fecha" name="dojo.dropdowndatepicker" /></td></tr>

<tr><td></td><td><input type="submit" value="Enviar"/></td></tr>

<input type="hidden" value="submit" />

</table>

</form>


Vamos a ver cada una de las partes:

<div id="error" style="color: red;"></div>

Aquí creamos un DIV de id error para agregar los errores de forma dinámica, esto es para no usar otra página web para desplegar el error. Esto es ideal para mostrar los errores al ingresar datos en un formulario o al querer loguearse a un sistema ya que no es necesario cargar toda una nueva página, es mucho mas rápido y mejora la usabilidad del sistema.

<form onsubmit="enviarDatosAjaxServlet(); return false;" >

Aquí comenzamos el form, donde tenemos el formulario de ingreso de datos. En vez de llamar directamente una página JSP o un Servlet, hacemos el llamado de una función javascript llamada enviarDatosAjaxServlet(), la cual veremos mas adelante.

<a:widget name="yahoo.tooltip" args="{context:['nombre']}" value="Ingrese su nombre aquí.">


Aquí tenemos un input de id nombre en el cual ingresamos el nombre de la persona que reclama. Si se fijan existe un tag extraño:

<a:widget name="yahoo.tooltip" args="{context:['nombre']}" value="Ingrese su nombre aquí.">


Ese tag corresponde a un widget de la bibliteca de Jmaki, específicamente a un tooltip Ajax de yahoo. Este tooltip lo encuentran en la paleta de Jmaki que pueden ver a la derecha del editor de NetBeans 6.1, simplemente lo arrastran y cambian el valor de context por el id del campo de datos, en este caso nombre.

El caso del textarea de comentario es igual al anterior, se arrastra el widget tootltip de yahoo y se cambia el valor de context a comentario.

<b>* Fecha:</b><a:widget id="fecha" name="dojo.dropdowndatepicker">


En el caso de la fecha utilizaremos un widget de Dojo llamado DropDownDatePicker el cual permite seleccionar la fecha desde un widget muy amistoso y bonito. Lo encuentran en la paleta de Jmaki y basta con arrastrar y soltar.

<input value="Enviar" type="submit">

<input value="submit" type="hidden">


Aquí simplemente agregamos el botón enviar y además la posibilidad de enviar los datos con solo presionar un Enter.



4.2 misFunciones.js

Hasta aquí todo va bien y muy fácil.

El tema ahora es obtener los datos del formulario y enviarlos al servidor para ser procesados. Este es el punto que más me costó. Primero fue difícil por que no sabia cómo obtener la fecha desde el DropDownDatePicker y segundo cómo obtener los caractéres especiales (ñs, tíldes, etc.). No den por sentado con que basta obtenerlos y enviarlos, si hacen eso verán que al otro lado los espacios aparecerán como %20 y cosas así.

A continuación veremos el código de la función enviarDatosAjaxServlet (el cual debe estar en el archivo misFunciones.js):

4.2.1 Función enviarDatosAjaxServlet

function enviarDatosAjaxServlet() {

//aqui cargo los valores seleccionados en los drop down date picker

var fecha = jmaki.getWidget('fecha').getValue();

//aqui obtengo el valor del comentario ingresado

var comentario = encodeURIComponent(document.getElementById("comentario").value);

//aqui obtengo el valor del nombre ingresado

var nombre = encodeURIComponent(document.getElementById("nombre").value);

//aqui envio la fecha, el comentario y el nombre al servlet MiServlet

//para almacenarlos en una sesion

jmaki.doAjax({ url : "MiServlet",

method : 'POST',

content : { 'nombre' : nombre,

'comentario' : comentario,

'fecha' : fecha

},

callback : function(req) {

procesarRespuesta(req);

}

});

}

El obtener la fecha en realidad es bastante simple, se utiliza la función jmaki.getWidget('fecha').getValue(); la cual retorna un String con la fecha seleccionada.

Para obtener los otros dos datos es necesario recurrir a document.getElementById("comentario").value (en el caso de obtener el comentario) y document.getElementById("nombre").value (en el caso de obtener el nombre). Lo segundo que hay que aplicar al resultado de esas “consultas” es la codificación de los caracteres, para esto utilizamos encodeURIComponent. De ésta manera aseguramos que al otro lado (en el servidor) los caracteres especiales lleguen como tales, pero se debe tener en cuenta que se debe hacer el proceso inverso (mas adelante lo veremos).
Para enviar los datos al servlet utilizamos una función de Jmaki llamada doAjax, la cual recibe como parámetros la url a la cual enviamos los datos, en este caso el servlet MiServlet, el tipo de método GET o POST, los datos que enviamos y el callback que se utiliza para obtener la respuesta del servlet.

En el lado del servidor obtendremos los datos a través del objeto request del servlet.

La función en callback llama a otra función llamada procesarRespuesta, la cual recibe como parámetro el request ajax (que viene con la respuesta del servlet).

La respuesta del servlet viene en formato XML, por lo que utilizaremos un responseXML para obtener la respuesta en vez de un responseText.



4.2.2 Función procesarRespuesta

function procesarRespuesta(respuesta) {

//aqui obtengo el archivo XML en una variable que envio el

//servlet como respuesta

var xml = respuesta.responseXML;

//aqui obtengo el valor del tag respuesta que esta en el xml

var res = valorXml(xml,'respuesta', 0);

//si la respuesta es ok significa que no hay errores

if(res == 'ok'){

window.location.href="resultado.jsp";

} else {

//si hay errores entonces muestro un mensaje

cargarContenidoDinamico('error', "Ocurrió un problema al enviar los datos, tal vez omitió el ingreso de alguno. Intente nuevamente.");

}

}


Lo primero es obtener el objeto xml desde el respuesta.responseXML.
Luego debemos obtener el valor de la respuesta que está entre los tag valor del xml y para tal tarea nos apoyamos en otra función llamada valorXML, que recibe como parámetro el objeto xml, el nombre del tag y el índice en el cual se encuentra el valor dentro del tag.

El valor se almacena en una variable llamada res (que corresponde a un String o cadena de caracteres) y se verifica si hay o no errores. Si no hay errores se redirige el navegador a la página resultado.jsp, en caso contrario se carga el error en el DIV “error” sin cambiar de página utilizando la función cargarContenidoDinamico.

4.2.3 Función valorXml

function valorXml(xml, tagName, index) {

//si el tag no existe retorno null, en caso contrario retorno el valor del tag

if(xml.getElementsByTagName(tagName).length == 0)

return null;

return xml.getElementsByTagName(tagName)[index].childNodes[0].nodeValue;


La función valorXml busca el valor correspondiente al indice index en el tag tagName. Si no lo encuentra retorna null, en caso contrario retorna el valor.

4.2.4 Función cargarContenidoDinamico

function cargarContenidoDinamico(contenedor, texto) {

//aqui agrego el texto al div contenedor

document.getElementById(contenedor).innerHTML = texto;

}


La función cargarContenidoDinamico carga el valor obtenido desde el xml en el DIV error para desplegar el mensaje de error sin cambiar de página.



4.3 Servlet MiServlet.java

El servlet, por default viene con tres métodos, doGet, doPost y processRequest. Es en este último donde codificaremos (los otros dos quedan intactos).
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/xml charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");

PrintWriter out = response.getWriter();

HttpSession sesion = request.getSession();
boolean error = false;

String fecha = request.getParameter("fecha");
if(fecha == null){
error = true;
}

String n = request.getParameter("nombre");
String nombre = null;
try{
nombre = URLDecoder.decode(n,"UTF-8");
if (nombre.equals(""))
error = true;
} catch (NullPointerException ex){
error = true;
}

String c = request.getParameter("comentario");
String comentario = null;
try{
comentario = URLDecoder.decode(c,"UTF-8");
if (comentario.equals(""))
error = true;
} catch (NullPointerException ex){
error = true;
}

if(error == false){
sesion.setAttribute("fecha", fecha);
sesion.setAttribute("nombre", nombre);
sesion.setAttribute("comentario", comentario);

out.println("ok");
} else{
out.println("error");
}

out.close();
}

Lo primero es fijarse que el contentType debe ser text/xml para indicar que devolveremos un xml.

En éste método creamos una sesión (que no tiene porque estar ligada a un user y password) para almacenar los datos ingresados previamente.

Como se dijo anteriormente, a través del request obtenemos los datos y los almacenamos en variables de tipo String.

El punto importante aquí es decodificar los datos codificados anteriormente para recuperar los caracteres especiales. Para ésto utilizamos el método estático URLDecoder.decode(n,"UTF-8"); el cual recibe como parámetro el String obtenido del request y el tipo de codificación. Será necesario importar la siguiente biblioteca: import java.net.URLDecoder;

La razón de almacenar los datos en una sesión es que ésta la utilizaremos en la página respuesta.jsp para desplegarlos en pantalla.

En el caso que no se tengan errores al obtener los datos, éstos se almacenan en la sesión y se envía la respuesta en xml simplemente agregando la linea out.println("ok");, y en caso contrario agregando out.println("error");.

De ésta forma enviamos la respuesta al callback de la función doAjax de Jmaki.



5 resultado.jsp

<%@ page contentType="text/html; charset=UTF-8"%>

<%@ page pageEncoding="UTF-8"%>

<%@ page language="java" session="true"%>

<%

response.setHeader("Cache-Control","no-store");

response.setHeader("Pragma","no-cache");

response.setDateHeader("Expires",0);

HttpSession sesion = request.getSession();

String comentario = (String) sesion.getAttribute("comentario");

String nombre = (String) sesion.getAttribute("nombre");

String fecha = (String) sesion.getAttribute("fecha");

sesion.invalidate();

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Resultado</title>

</head>

<body>

<h1>Resultado</h1>

<table>

<tr><td><b>El nombre ingresado es:</b></td><td><%=nombre%></td></tr>

<tr><td><b>El comentario ingresado es:</b></td><td><%=comentario%></td></tr>

<tr><td><b>La fecha de ingreso del comentario es:</b></td><td><%=fecha%></td></tr>

</table>

</body>

</html>


Lo primero que hacemos aquí es definir la utilización de la sesión agregando el tag
<%@page language=”java” session=”true”%>

Luego obtenemos la sesión desde el request y obtenemos los datos, después invalidamos la sesión eliminando los datos de ésta.

Después simplemente mostramos los datos agregandolos con el tag <%=valor%>.



6 Conclusiones

Con ajax podemos crear una presentación mas decente de los datos y a la vez mas eficiente si nos preocupamos de que las funciones que carguemos sean livianas y de poco texto.

Gracias a las funciones expuestas en este simple demo, podemos cargar datos de forma dinámica para la muestra de errores sin necesidad de cargar una nueva página, lo que es mas rápido y eficiente.

También vimos que gracias a las funciones de la biblioteca de Jmaki podemos agregar widgets bonitos y que mejoran la usabilidad del sistema.

Y por último aprendimos a comunicar tecnologías distintas como son java con javascript (Ajax), manejando caracteres especiales, etc.

7 Anexos


Espero les sea de utilidad.

El código me lo piden dejando su correo en el blog y se los enviaré a la brevedad.
También tengo esta entrada del blog en un documento ODT y PDF (no me pidan formato DOC), se los reboto junto al código.

saludos !!!

EDITADO!!!

Subí el código a un servidor para que puedan hacer la descarga sin necesidad de enviarme sus correos (así no tienen que esperar a que yo lea el correo para rebotarlo a ustedes), si tienen problemas para la descarga seguimos con el método tradicional del correo electrónico.

Descarga

saludos !!!!