ARCmop Java Programacion Solaris Linux

Java-linux-programacion

Comet con jquery y Webservlet (JEE 6) (Ajax Reverse)

Posted by Cmop en marzo 18, 2012

Hola a todos, bueno este artículo ha estado mucho tiempo en borrador (desde noviembre del 2011) y pues ahora explicaré como implementar comet usando jquery.

Bueno un poco de teoría:

CometEn el desarrollo web, Comet es un término para describir un modelo de aplicación web en el que una petición HTTP es mantenida abierta, lo que permite a un servidor web enviar datos a un navegador por Tecnología Push, sin que el navegador los solicite explícitamente. Comet es un término que engloba múltiples técnicas para conseguir esta interacción. Todos estos métodos confían en características incluidas por defecto en navegadores, como JavaScript, en lugar de sobre plugins no disponibles por defecto. (Wikipedia)

Según wikipedia, Facebook y Gmail utilizan ésta tecnica.

He implementado Comet a través de dos técnicas: usando ajax y usando un iframe.

Las herramientas utilizadas son:

  1. Netbeans
  2. Tomcat
  3. Jquery
  4. JSP
  5. Navegadores: Firefox, Chrome, safari, Opera, IE
El encargado de gestionar las conexiones entrantes en un servlet, que se puede generar utilzando la anotacion @Webservlet incluida en JEE 6, veamos su código cuando usamos AJAX:
package micomet;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import java.io.PrintWriter;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.servlet.AsyncContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static helper.HelperFunciones.toJsonp;

@WebServlet(urlPatterns = {"/cometajax"}, asyncSupported = true)
public class AjaxCometServlet extends HttpServlet {

private static final Queue<AsyncContext> colaContextosAsincronos = new ConcurrentLinkedQueue<AsyncContext>();
 private static final BlockingQueue<String> colaMensajes = new LinkedBlockingQueue<String>();
 private static final String INICIO_SCRIPT_TAG = "<script type='text/javascript'>\n";
 private static final String FIN_SCRIPT_TAG = "</script>\n";
 private Thread HiloNotificador = null;

@Override
 public void init(ServletConfig config) throws ServletException {
 Runnable RunnableNotificador = new Runnable() {

@Override
 public void run() {
 boolean ejecucion = false;
 while (!ejecucion) {
 String mensaje = null;
 try {
 mensaje = colaMensajes.take();
 for (AsyncContext ac : colaContextosAsincronos) {
 try {
 PrintWriter clienteEscritor = ac.getResponse().getWriter();
 clienteEscritor.println(mensaje);
 clienteEscritor.flush();
 ac.complete();
 colaContextosAsincronos.remove(ac);
 } catch (IOException ex) {
 colaContextosAsincronos.remove(ac);
 }
 }
 } catch (InterruptedException iex) {
 ejecucion = true;
 }
 }
 }
 };
 HiloNotificador = new Thread(RunnableNotificador);
 HiloNotificador.start();
 }

@Override
 protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

final AsyncContext ac = req.startAsync();
 ac.setTimeout(24 * 60 * 60 * 1000);

 ac.addListener(new AsyncListener() {

 @Override
 public void onComplete(AsyncEvent event) throws IOException {
 colaContextosAsincronos.remove(ac);
 }

 @Override
 public void onTimeout(AsyncEvent event) throws IOException {
 colaContextosAsincronos.remove(ac);
 }

 @Override
 public void onError(AsyncEvent event) throws IOException {
 colaContextosAsincronos.remove(ac);
 }

 @Override
 public void onStartAsync(AsyncEvent event) throws IOException {
 }
 });
 colaContextosAsincronos.add(ac);

res.setContentType("text/html");
 res.setHeader("Cache-Control", "private");
 res.setHeader("Pragma", "no-cache");

 }

@Override
 @SuppressWarnings("unchecked")
 protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/plain");
 res.setHeader("Cache-Control", "private");
 res.setHeader("Pragma", "no-cache");

req.setCharacterEncoding("UTF-8");
 String action = req.getParameter("action");
 String name = req.getParameter("name");

if ("login".equals(action)) {
 String cMessage = INICIO_SCRIPT_TAG + toJsonp("Mensaje del Sistema", name + " ha iniciado sesion") + FIN_SCRIPT_TAG;
 notificarNuevoMensaje(cMessage);
 res.getWriter().println("success");
 } else if ("post".equals(action)) {
 String message = req.getParameter("message");
 String cMessage = INICIO_SCRIPT_TAG + toJsonp(name, message) + FIN_SCRIPT_TAG;
 notificarNuevoMensaje(cMessage);
 res.getWriter().println("success");
 } else {
 res.sendError(422, "Unprocessable Entity");
 }
 }

@Override
 public void destroy() {
 colaContextosAsincronos.clear();
 HiloNotificador.interrupt();
 }

private void notificarNuevoMensaje(String cMessage) throws IOException {
 try {
 colaMensajes.put(cMessage);
 } catch (Exception ex) {
 throw new IOException(ex);
 }
 }
}

Y cuando se usa un IFRAME para la técnica.


package micomet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static helper.HelperFunciones.toJsonp;

@WebServlet(urlPatterns = {"/cometiframe"}, asyncSupported = true)
public class IframeCometServlet extends HttpServlet {

private static final Queue<AsyncContext> colaContextosAsincronos = new ConcurrentLinkedQueue<AsyncContext>();
 private static final BlockingQueue<String> colaMensajes = new LinkedBlockingQueue<String>();
 private static final String INICIO_SCRIPT_TAG = "<script type='text/javascript'>\n";
 private static final String FIN_SCRIPT_TAG = "</script>\n";
 private Thread HiloNotificador = null;

@Override
 public void init(ServletConfig config) throws ServletException {
 Runnable RunnableNotificador = new Runnable() {

@Override
 public void run() {
 boolean ejecucion = false;
 while (!ejecucion) {
 String mensaje = null;
 try {
 mensaje = colaMensajes.take();
 for (AsyncContext ac : colaContextosAsincronos) {
 try {
 PrintWriter clienteEscritor = ac.getResponse().getWriter();
 clienteEscritor.println(mensaje);
 clienteEscritor.flush();
 } catch (IOException ex) {
 colaContextosAsincronos.remove(ac);
 }
 }
 } catch (InterruptedException iex) {
 ejecucion = true;
 }
 }
 }
 };
 HiloNotificador = new Thread(RunnableNotificador);
 HiloNotificador.start();
 }

@Override
 protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 final AsyncContext ac = req.startAsync();
 ac.setTimeout(24 * 60 * 60 * 1000);
 ac.addListener(new AsyncListener() {

@Override
 public void onComplete(AsyncEvent event) throws IOException {
 colaContextosAsincronos.remove(ac);
 }

@Override
 public void onTimeout(AsyncEvent event) throws IOException {
 colaContextosAsincronos.remove(ac);
 }

@Override
 public void onError(AsyncEvent event) throws IOException {
 colaContextosAsincronos.remove(ac);
 }

@Override
 public void onStartAsync(AsyncEvent event) throws IOException {
 }
 });
 colaContextosAsincronos.add(ac);

 res.setContentType("text/html");
 res.setHeader("Cache-Control", "private");
 res.setHeader("Pragma", "no-cache");
 }

@Override
 @SuppressWarnings("unchecked")
 protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 res.setContentType("text/plain");
 res.setHeader("Cache-Control", "private");
 res.setHeader("Pragma", "no-cache");

req.setCharacterEncoding("UTF-8");
 String action = req.getParameter("action");
 String name = req.getParameter("name");

if ("login".equals(action)) {
 String cMessage = INICIO_SCRIPT_TAG + toJsonp("Mensaje del Sistema", name + " ha iniciado sesion") + FIN_SCRIPT_TAG;
 notificarNuevoMensaje(cMessage);
 res.getWriter().println("success");
 } else if ("post".equals(action)) {
 String message = req.getParameter("message");
 String cMessage = INICIO_SCRIPT_TAG + toJsonp(name, message) + FIN_SCRIPT_TAG;
 notificarNuevoMensaje(cMessage);
 res.getWriter().println("success");
 } else {
 res.sendError(422, "Unprocessable Entity");
 }
 }

@Override
 public void destroy() {
 colaContextosAsincronos.clear();
 HiloNotificador.interrupt();
 }

private void notificarNuevoMensaje(String cMessage) throws IOException {
 try {
 colaMensajes.put(cMessage);
 } catch (Exception ex) {
 throw new IOException(ex);
 }
 }
}

Básicamente el funcionamiento es:

  • Tener una cola e ir guardando las peticiones en ella.
  • Cuando llega un nuevo mensaje, enviar mensaje a todas las conexiones de la cola y si hay alguna excepción, eliminar dicha conexion y continuar con la siguiente.
  • Una linea interesante es: final AsyncContext ac = req.startAsync(); es ésta línea la que nos permite mantener abiertas las conexiones para comunicarnos luego.
Ahora he colocado 2 videos de demostración usando Comet.
USANDO IFRAME
USANDO AJAX

Bueno después de ver los vídeos ustedes verán, cual técnica desean usar.

Aqui los fuentes, deben cambiar el nombre y quitarle “.doc” que esta al final, ya que los archivos en son “tar.gz”

  • Usando iframe: https://cmop17.files.wordpress.com/2012/03/comettecnicaiframe-tar-gz.doc
  • Usando Ajax: https://cmop17.files.wordpress.com/2012/03/comettecnicaajax-tar-gz.doc

Fuentes informativas:

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: