Comet en Glassfish/Grizzly con Ajax o iframe
La verdad es que el ejemplo de Jean-Francois me parecía demasiado complejo y me quede algo extrañado por el uso de un iframe como cliente del Comet... con todo lo que se habla de AJAX y las posibilidades de Comet, me parecía raro que "volvieramos" a usarlo.
La verdad, es que se nota bastante que esto del Comet es muy nuevo y no hay mucha información en internet, pero he podido encontrar cosas interesantes y razones por las cuales usar iframes o AJAX como cliente de Comet. Tengo que advertir, que si por algo no me destaco es por mis capacidades con javascript, pero conozco un poco y me han hablado bien siempre de Prototype, así que el objetivo era que el cliente AJAX fuera realizado con este framework. Empezemos:
Ya vimos lo sencilla que era la implementación de Comet en Grizzly, pero repito explicación sobre mi ejemplo que tiene menos funcionalidades.
1.- Registramos el comet bajo un nombre. Puede coincidir con una URL para dar sentido a las operaciones pero no es algo obligatorio. Esta operación se debe realizar antes que ninguna otra, por lo que su ubicación natural es el método init de un Servlet. Si además configurames el servlet para que se inicie al arrancar nuestra aplicación con la etiqueta <load-on-startup>0</load-on-startup> nos evitaremos problemas.
public void init (ServletConfig config) throws ServletException
{
super.init(config);
//comet = servlet context path pero no es obligatorio
contextPath = config.getServletContext()
.getContextPath() + "/subscription";
CometEngine cometEngine = CometEngine.getEngine();
context = cometEngine.register(contextPath);
context.setExpirationDelay(40 * 1000); //segundos de delay
}
2.-El Servlet solo va a realizar dos operaciones registrar al cliente en el Comet y enviar notificaciones a todos los clientes conectado. Para enviar la notificación se debe enviar el parametro notificar al Servlet. Para garantizar que un cliente no intenta conectarse dos veces, se crea una variable de sesión, conectado, que se comprueba que no exista antes de conectarse.
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
if (request.getParameter("notificar")==null
&& request.getSession().getAttribute("conectado")==null)
{//conecto el cliente al comet
response.setContentType("text/html");//muy importante
EjemploCometHandler handler = new EjemploCometHandler();
andler.attach(response.getWriter());
int handlerId = context.addCometHandler(handler);
request.getSession().setAttribute("conectado",
Integer.valueOf(handlerId));
response.getWriter().print("Conectado al Commet
");
response.getWriter().flush();
//no cerramos la conexion.. que de eso se trata
}else if (request.getParameter("notificar")!=null){
context.notify("Envio a todos los usuarios conectados al comet
");
}
return;
}
3.- El CometHandler implementado para el ejemplo se limita a enviar los mensajes de notificación y a informar de la desconexión
public class EjemploCometHandler implements CometHandler{
private PrintWriter printWriter=null;
public void attach(PrintWriter printWriter) {
this.printWriter = printWriter;
}
public void onEvent(CometEvent event) throws IOException {
printWriter.println(event.attachment());
printWriter.flush();
}
public void onInitialize(CometEvent event) throws IOException {
printWriter.println("onInitialize");
printWriter.flush();
}
public void onTerminate(CometEvent event) throws IOException {
printWriter.println("onTerminate");
printWriter.flush();
printWriter.close();
}
public void onInterrupt(CometEvent event) throws IOException {
printWriter.println("onInterrup");
printWriter.flush();
printWriter.close();
}
}
Con esto ya tenemos desarrollada la parte del servidor, ahora vamos al cliente. Como decía al principio hay dos posibilidades AJAX o iframe. Todo apunta a que la mejor opción es la del iFrame, por varias razones:
- Un navegador solo puede abrir dos conexiones HTTP desde una página/frame, por lo que si mantenemos una abierta para el Comet, solo nos queda una libre para realizar llamadas.
- El iframe funciona en todos los navegadores mientras que AJAX en InternetExplorer parece tener problemas con Comet, ya que en este navegador AJAX no es consciente de la información que va llegando hasta que se cierra la conexión :-(
Veamos el código del ejemplo del cliente con el iframe, por un lado tenemos el iframe que se conecta al comet. El boton realiza un llamada Ajax que actualiza el iframe de la misma página y la de todos los clientes conectados:
<html>
<head><title>Ejemplo Comet iFrame</title>
<script src="prototype.js" type="text/javascript"></script>
</head>
<body>
<iframe src="subscription" name="comet" width="100%"></iframe>
<script type="text/javascript" language="javascript">
// <![CDATA[
function notificarComet(){
var url = 'subscription';
new Ajax.Request( url, {
method: 'get',
parameters: 'notificar=true'
});
}
// ]]>
</script>
<input type="button" name="a" value="notificar"
onclick="notificarComet();"/>
</body>
</html>
La única forma que he encontrado para hacer los mismo con Prototype y sabiendo que solo funciona en firefox es la siguente, tras hacer la petición y conectar al Comet, es necesario introducir el cliente en un bucle que vaya comprobando si se ha enviado información desde el Comet:
<html>
<head><title>Ejemplo Comet Ajax</title>
<script src="prototype.js" type="text/javascript"></script>
<script type="text/javascript" language="javascript">
// <![CDATA[
function comet(){
var url = 'subscription';
var listener=null;
new Ajax.Request(
url,
{ onInteractive: function(xhr){
if (!listener){
listener=new PeriodicalExecuter(
function(){
$('update').update(xhr.responseText);
},
1 /* check for changes every 1 second*/
);
}
}
}
);
}
function notificarComet(){
var url = 'subscription';
new Ajax.Request( url, {
method: 'get',
parameters: 'notificar=true&t='+new Date().getTime()
});
}
// ]]>
</script>
</head>
<body onload="comet();">
<div id="update"></div>
<input type="button" name="a" value="notificar"
onclick="notificarComet();"/>
</body>
</html>
No parece que Prototype se haya preparado para Comet. Sin embargo otro framework javascript muy conocido, Dojo, esta muy integrado con un servidor Comet, www.cometd.org, y con el no hay que recurrir a trucos como en Prototype, ya que cuenta con la operación dojo.io.bind() que me temo habrá que probar en próximos capítulos. Mientras os dejo el ejemplo para que lo probéis.






