Archivo de la etiqueta: R.I.A.

Adobe Air XII – No se puede instalar la aplicación, este instalador no se ha configurado correctamente

Recientemente he tenido que modificar una aplicación AIR y, ya de paso, me dio por convertirla a Air2. Todo iba bien hasta que la subí al servidor para que a todos los usuarios les apareciese automáticamente la actualización pero una vez comenzaba la instalación salía el siguiente mensaje:

No se puede instalar la aplicación, este instalador no se ha configurado correctamente

Tras googlear un poco lo único que encontré es que normalmente se debe a que la has publicado con un certificado distinto al que tenía la versión anterior, pero no era mi caso. Y así, de repente, me acordé que para utilizar la instalación automática se necesitaba el publisherID y pensé, ¿no será algo de eso?.

Así es que revisé el descriptor de la aplicación Aplicacion-app.xml y me encontré con un nuevo nodo comentado:

<publisherID></publisherID>

Lo descomenté y le añadí mi publisherID que se obtiene de la versión anterior instalada en:

RUTA_DE_INSTALACION_DE_TU_APPMETA-INFAIRapplication.xml

Publicas de nuevo la aplicación y todo solucionado, se actualiza perfectamente :).

Adobe Air XI – Despliega tus aplicaciones directamente desde la web

Vuelvo con un artículo sobre la serie que hice hace algún tipo acerca de Adobe Air.

Uno de los principales problemas con los que me he encontrado al desarrollar con Air es a la hora del despliegue, ya que los clientes deben instalarse primero el framework y después la aplicación, lo que da lugar a muchas dudas y potenciales problemas. Para solucionarlo existe una librería de Adobe que permite saber si un cliente tiene instalado el framework y tu aplicación directamente desde una página web y lanzar la instalación de ambos en caso de que no los tenga, de esta manera los usuarios no tienen que descargar nada, todo muy sencillo e intuitivo.

Para interactuar con esta librería existe además una pequeña aplicación Flash que se distribuye con el sdk de Flex con los conceptos básicos: comprobar si el cliente tiene el framework y lanzar la instalación si es necesario. La podéis encontrar en:

C:Archivos de programaAdobeAdobe Flash Builder 4sdks4.0.0samplesbadge

Sin embargo puede ser realmente tedioso configurarla y programarla adecuadamente, así que existe Badger, una aplicación AIR que enmascara todo este proceso con un sencillo asistente donde rellenas los parámetros, incluidas las cadenas de texto a utilizar, y te genera el código necesario. Puedes descargarla aquí.

Al ejecutarla veremos la pantalla principal.

Por defecto solo hay un template, así que el primer menú lo podemos obviar. Con el segundo seleccionas tu aplicación AIR para que Badger cargue automáticamente algunos de los parámetros y, cuando has terminado, con la última opción “Export badge” generas el código.

Dentro de la configuración hay dos campos importantes. “application url“, debes introducir la url completa (con http://) desde donde se descargará la aplicación, y “publisher ID“, donde tienes que seleccionar la ruta donde la tienes instalada localmente para que Badger lea automáticamente este dato, imprescindible para comprobar si el cliente ya la tiene instalada.

Al subir todos los archivos generados a tu web (el html puedes integrarlo adecuadamente) y cargar la url en un navegador verás algo parecido a esto suponiendo que no tienes la aplicación (o el framework) instalados.

Al hacer click en “Instalar”, si no tienes el framework instalado, te pedirá autorización y comenzará la descarga:

Para finalmente acabar instalando tu aplicación.

Si el cliente ya tuviese la aplicación instalada podremos lanzarla automáticamente también. Para conseguirlo es necesario, primero, que en el descriptor de la aplicación hayas configurado el siguiente parámetro a “true“:

<allowBrowserInvocation>true</allowBrowserInvocation>

Ahora, si el cliente ya tiene nuestra aplicación, veremos algo como esto:

Y al hacer click en “Lanzar ahora” nuestra aplicación se cargará ella sola :).

Como veis es muy sencillo para el cliente instalar así nuestras aplicaciones AIR, nada que ver con tener que descargar el framework por un lado y la aplicación por el otro.

Adobe Flash Catalyst, integración completa entre diseño y desarrollo de RIA’s

El artículo anterior me ha llevado a preparar otro sobre Flash Catalyst porque creo que marcará un antes y un después en el desarrollo de aplicaciones. Ojo, no he dicho nada de desarrollo web sino de aplicaciones, sean online o de escritorio (AIR).

Flash Catalyst se encuentra ahora en su beta2, no conozco exactamente el roadmap definitivo pero imagino que la versión final saldrá a principios del próximo año.

La importancia de este producto es seguramente mayor a la que cabría esperar ya que estos días se ha sabido que habrá soporte nativo para Flash en el iPhone y que antes de final de año probablemente lo haya también para los sistemas operativos más populares para móviles (Windows Mobile, Palm webOS, Android y Symbian OS). Hablamos de Flash 10.1, no de Flash Lite. Menuda revolución será poder desarrollar juegos y aplicaciones para móviles sin Java, probablemente mucho más vistosas y compatibles.

Pero vamos al objeto de este artículo. Como he comentado,  vamos por la segunda beta de Flash Catalyst. La probé ayer mismo y por casualidad. Al hilo de mi anterior artículo llegué a casa dispuesto a comprobar qué era aquello que había comentado el profesor del curso, recordaba haber instalado la primera beta pero no había hecho absolutamente nada con ella. Entonces entendí el porqué. Era una aplicación imposible de manejar, pesadísima, lentísima, consumía prácticamente todos los recursos de tu equipo. En su momento no pude ni darle una oportunidad, pero ayer, al ver que había una nueva beta, decidí probarla, ya que en las “release notes” uno de los puntos más destacados que se comentaban eran las considerables mejoras en el rendimiento global de la aplicación. Y así es, con esta beta sí que ya te apetece probar el producto y descubrir una herramienta fantástica, en serio. Pero no lo olvidemos, una herramienta para diseñadores, o mejor aún, para que los diseñadores y los programadores colaboren estrechamente y que permitirá crear aplicaciones con interfaces de usuario increíblemente avanzadas, y es que ésta, aunque no lo parezca, es la finalidad de Catalyst, llegar al siguiente escalón donde Flex no llegaba y, de paso, poner de nuevo en el meollo del trabajo a los diseñadores, algo abandonados por Adobe los últimos años.

Ojo a un detalle. Adobe presentó por primera vez esta herramienta en octubre de 2007 en el Adobe Max y la presentó funcionando, pero aún así han tardado más de dos años en ponerla a punto.

La teoría de Catalyst es sencilla. Que el diseñador diseñe y que el programador programe, y que para hacerlo no tengan que aprender ninguno de los dos casi nada nuevo. El diseñador seguirá trabajando con su Photoshop o su Illustrator de toda la vida y el programador hará lo propio con Flex. Entre los dos mundos se sitúa Catalyst.

catalyst

Una vez el diseñador tiene la aplicación bien diseñada y preparada en Photoshop, importa directamente el PSD en Catalyst, con lo cual se importan correctamente las características que cada capa tenía allí. En este momento aparece la verdadera utilidad. Desde Catalyst se convierten los elementos del diseño inicial en componentes de Flex (botones, cajas de texto, listas…) con sólo hacer click en un menú. Se pueden incluso añadir ciertos eventos, cambios de estado, transiciones, efectos… todo de manera gráfica e intuitiva sin necesidad de escribir código alguno (sí, en serio). En realidad parece que lo que hace Catalyst es poquísimo, ¿cómo han podido tardar tantos años en hacer esto? Si has programado en Flex o has tenido que convertir un diseño desde Photoshop, sabrás lo que supone. Desde el propio Catalyst puedes probar el proyecto, se convierte a swf, incluso lo puedes compilar directamente en una aplicación AIR, pero lo mejor de todo es que los proyectos Catalyst se importan directamente en Flex4 para que el programador le añada toda la funcionalidad necesaria.

Digamos que es una herramienta a medio camino entre el entorno de animación Flash de toda la vida y Flex. Con Catalyst (creo) no podrás hacer animaciones ni virguerías varias, pero crearás aplicaciones con una interfaz de usuario impresionante, justo lo que hasta ahora era, si no imposible, complicadísimo.

La aplicación hace lo que quiere el programador con la apariencia que quiere el diseñador. Esto, hasta hoy, era bastante utópico.

Primeros pasos con Flash Media Server (2), autentificando usuarios del chat

Hace unos días veíamos como crear un sencillo chat con FMS.

Hoy, a petición popular, vamos a añadir la primera mejora a nuestra aplicación. Por defecto habíamos diseñado un chat público, cualquiera podía conectarse. Sin embargo, puede que deseemos que sólo nuestros usuarios registrados puedan utilizarlo. Necesitamos entonces que nuestros clientes se puedan autentificar contra nuestra base de datos pero no podemos dejar esa responsabilidad en manos de la aplicación cliente, cualquiera podría hacer otra que se conecte a nuestro servidor sin requerir autentificación.

FMS no puede conectarse con ningún tipo de bases de datos, no es su función, aunque cada vez hay más gente solicitando esta funcionalidad y se espera que en la próxima versión (FMS4) haya alguna novedad al respecto. Sea como fuera, de momento no hay métodos nativos para acceder a bases de datos así que sólo se puede recurrir a lo que un día fue Flash Remoting y hoy son los servicios RPC bajo AMF, el método estándar de acceso a datos desde aplicaciones Flash (con el permiso de los webservices SOAP). Con el tiempo fueron apareciendo versiones en los distintos lenguajes para utilizar el protocolo AMF fuera del amparo de Macromedia (ahora Adobe), pero sin duda uno de los pioneros fue amfphp, el que nos permite comunicar aplicaciones Flash con el servidor a través de PHP.

AMF es un formato para serialización de datos e invocación de métodos remotos y es independiente al lenguaje con el que trabaja el servidor, simplemente debe cumplir las especificaciones. FMS, como no podía ser de otra forma, puede comunicarse con nuestro servidor gracias a este protocolo AMF y esto es precisamente lo que vamos a utilizar para autentificar a nuestros usuarios:

dibujo1.jpg

El proceso es mucho más sencillo de lo que parece.

Comenzaremos por descargar amfphp desde la web oficial.  Lo descomprimes y subes la carpeta “amfphp” a tu servidor en la ruta que desees. Ya tienes lo que es el cerebro del sistema listo, para comprobarlo prueba la URL:

http://tuservidor/amfphp/gateway.php

Si todo va bien, prueba ahora el navegador de servicios, herramienta imprescindible para probar tus métodos remotos:

http://tuservidor/amfphp/browser/

Si no has visto ningún error es que todo va bien. Veamos ahora como crear tu servicio de autentificación.

Dentro de la carpeta amfphp que has subido a tu servidor encontrarás la carpeta services. Crea dentro de ella un nuevo directorio con el nombre de tu aplicación, por ejemplo chat. Creamos ahora dentro de esa carpeta las clases y métodos que se necesiten para crear el servicio de autentificación. Por ejemplo, en mi caso creo Chat.php:

<?
class Chat{
    var $conn;    function Chat(){
      $this->conn=mysql_connect("host", "usuario", "clave");
      mysql_select_db('db', $this->conn);
    }
function login($nick, $pass){
        $rs=mysql_query("select * from usuarios where nick='$nick' AND pass='$pass'");
        if(mysql_num_rows($rs)>0)
          return 1;
        else
          return 0;
    }
}
?>

Sencillo ¿no?. Nuestra clase tiene un método login que nos dice si la pareja nick/clave existe en nuestra base de datos. Obviamente esto es sólo un ejemplo, en tu aplicación haz la clase con todos los métodos y la lógica que necesites.

Si ahora vas de nuevo al Service Browser verás que te aparece la nueva aplicación chat con la clase que has creado. Si accedes a ella puedes probar el método login pasándole los parámetros necesarios. Más sencillo imposible.

Ya tenemos la lógica de autentificación lista. ¿Cómo le decimos ahora a FMS que la utilice? Modificamos el archivo main.asc que habíamos hecho en el capítulo anterior para que antes de aceptar la conexión de un usuario la autentifique.

application.onConnect = function(client, nick, clave, sala){
    var valido=1;
    for( i in this.userList){
        if(String(this.userList[i].nick).toLowerCase()==String(nick).toLowerCase()){
            valido=0;
            break;
        }
    }
    if(valido==1){
        var myServer = NetServices.createGatewayConnection("http://tudominio/amfphp/gateway.php");
        var servicio = myServer.getService("chat.Chat", this);
        var callserver=servicio.login(email, clave);

        this.login_Result=function(result){
            //trace("RESULTADO LOGIN"+result);
            if(result>0){
                client.uid = this.uid;
                client.nick = nick;
                client.sala = sala;
                this.acceptConnection(client);
                this.userList[client.uid] = client;
                this.users_so.setProperty(client.uid, client);
                var msg = "*** Entra " + client.nick + " ***";
                this.chat_so.send("onMsgSend", msg);
                client.call("onUserid", null, client.uid, client.nick);
                this.uid++;
            }else{
                var err = new Object();
                err.message = "Email o contraseña incorrectos";
                application.rejectConnection(client, err);
            }
        }
    }else{
        var err = new Object();
        err.message = "Ya hay un usuario con ese nick";
        trace(err.message);
        application.rejectConnection(client, err);
    }
}

Es tan simple como añadir la comprobación del usuario y clave de nuestro cliente. Indicamos la url de nuestro gateway (amfphp) y llamamos al método login con los datos correspondientes. Lo más importante es la forma en que obtenemos los datos que devuelve el metodo. La llamada es asíncrona, no se paraliza la actividad esperando por un resultado sino que se continua la actividad esperando a recibir la respuesta. Para eso se supone que debe haber definida una función nombremetodoremoto_Result, en nuestro caso login_Result ya que login es el método remoto al que llamamos. Cuando se llame a esta función sabremos si el usuario existe en nuestra base de datos, con lo que aceptamos su conexión al chat, o no existe y la rechazamos.

Como habéis visto, es muy sencillo llamar a métodos remotos desde FMS de manera que aumenten las posibilidades de tu aplicación ya que puedes hacer uso de bases de datos y de sistemas avanzados y siempre de manera muy simple.

Yo te he dado las pistas de cómo hacerlo, el resto queda a tu imaginación.

Primeros pasos con Flash Media Server, creando un chat

Hoy vamos a ver cómo crear una sencilla aplicación de chat con Flex. No debe tomarse este ejemplo como una aplicación completa sino simplemente como un método para comprender cómo funcionan los distintos elementos que intervienen. Tampoco debe compararse este proyecto con el cliente IRC que os presenté hace unos meses, aunque ámbos son aplicaciones de chat,  son sistemas distintos, como veréis más adelante.

Chat en Flex con FMS

Flash Media Server (FMS, antiguamente conocido Flash Communication Server) es el software de Adobe que permite crear aplicaciones multiusuario de una manera sencilla y bajo tecnología Flash. FMS permite no sólo intercambiar y sincronizar mensajes de texto entre los clientes sino tambien audio y vídeo. Si flv es el sistema de vídeo por excelencia en Internet, FMS es la tecnología para crear sistemas multimedia multiusuario. La principal pega de FMS es que es de pago, y no precisamente barato, sin embargo existe una licencia “developer” que te permite utilizar el 100% de las posibilidades del servidor, la única limitación es la cantidad de usuarios concurrentes que puedes tener, diez en este caso. Existe una alternativa Open Source a FMS, Red5, pero no lo he utilizado nunca así que no puedo opinar. La teoría con Red5 es exactamente la misma que con FMS, sólo cambia el medio (FMS se programa con ActionScript y Red5 con Java). Como ejemplo de aplicaciones que utilizan FMS está Yahoo Live, la aplicación de grabación directa desde tu webcam de Youtube o todos, absolutamente todos los videochats que puedes encontrar en Internet.

Como lo más probable es que no tengas una licencia de FMS, trabajaremos con la versión developer que hemos indicado ya que nos permitirá poner en práctica todos los conceptos necesarios.  Lo primero que debes hacer es, por tanto, descargar e instalar FMS, hay versiones para servidores Linux y Windows, escoge la que mejor te venga. La instalación no tiene ningún secreto así que lo dejaré en vuestras manos. En mi caso, como siempre, un CentOS5.

Los proyectos creados sobre FMS son sistemas cliente/servidor y necesitan dos aplicaciones para poder funcionar:

  1. Aplicación cliente, sería la parte Flash pura y dura, la que utilizarán los usuarios.
  2. Aplicación servidor, sería lo que corre bajo FMS y a lo que se conecta la aplicación cliente.

Digamos que FMS es la varita mágina del conjunto ya que hace completamente transparente al desarrollador la conexión entre usuarios y la inyección de mensajes entre ellos. Para entenderlo pensemos en un chat donde los usuarios se conectan al servidor y cada vez que uno de ellos escribe un mensaje éste se envía automáticamente a los demás usuarios. Imagina, además, que no sólo hablamos de texto sino que cuando un usuario enciende su webcam los demás usuarios pueden verla. Todo esto lo hace FMS por ti, sólo debes preocuparte de construir tu aplicación.

Programando una aplicación FMS

Empezaremos la parte práctica creando el código necesario en el servidor. Por defecto las aplicaciones FMS de instalan bajo /opt/adobe/fms/applications creando un nuevo directorio con el nombre de tu aplicación, en nuestro caso la llamaremos textchat.

Las aplicaciones se cargan y se mantienen en memoria una vez algún usuario accede por primera vez a la misma y, cuando pasado un tiempo prudencial no hay usuarios, se descarga para liberar recursos esperando a que otro usuario vuelva a acceder. Cada vez que se hacen cambios a la parte servidor de la aplicación hay que reiniciarla, pero no es necesario reiniciar el servidor FMS para reiniciar una aplicación, basta con descargar y volver a cargar esa aplicación desde la consola de administración como veremos más adelante. Desde esta misma consola podremos ver también la salida de los comandos trace que ocurran en el servidor y los objetos creados y su contenido, ayuda imprescindible mientras estás desarrollándola. Veremos también como activar la salida de estas opciones puesto que por defecto (por seguridad) vienen deshabilitadas.

Por defecto todas las aplicaciones deben tener un archivo main.asc con el código necesario.  Crea tu main.asc de esta manera:

load("netservices.asc");

application.onAppStart = function(){
  trace("inicia text chat");
  this.users_so = SharedObject.get("users_so", false);
  this.chat_so = SharedObject.get("chat_so", false);
  this.userList = {};
  this.uid = 1;
}

application.onConnect = function(client, nick, clave, sala){
  trace(nick+" "+clave+" "+sala);
  var valido=1;
  for( i in this.userList){
    if(String(this.userList[i].nick).toLowerCase()==String(nick).toLowerCase()){
      valido=0;
      break;
    }
  }
  if(valido==1){
    client.uid = this.uid;
    client.nick = nick;
    client.sala = sala;
    this.acceptConnection(client);
    this.userList[client.uid] = client;
    this.users_so.setProperty(client.uid, client);
    var msg = "<font color='#666666'><b>*** Entra " + client.nick + " ***</b></font><br>";
    this.chat_so.send("onMsgSend", msg);
    client.call("onUserid", null, client.uid, client.nick);
    this.uid++;
  }else{
    var err = new Object();
    err.message = "Ya hay un usuario con ese nick";
    trace(err.message);
    application.rejectConnection(client, err);
  }
}

application.onDisconnect = function(oldClient){
  if(oldClient.uid!=undefined){
    trace("Descontecta cliente. UID:  " +oldClient.uid);
    var msg = "<font color='#666666'><b>*** " + oldClient.nick + " sale ***</b></font><br>";
    this.chat_so.send("onMsgSend", msg);
    for( i in application.userList){
      if(Number(application.userList[i].uid)==Number(oldClient.uid)){
        application.userList[i]=undefined;
      }
      k++;
    }
    application.disconnect(oldClient);
    application.users_so.setProperty(oldClient.uid, null);
  }
}

El funcionamiento es muy sencillo. Nuestra aplicación maneja tres eventos:

  1. onAppStart: Se dispara cuando la aplicación se carga en memoria por primera vez.
  2. onConnect: Se lanza cuando un usuario se conecta a la aplicación.
  3. onDisconnect:Cuando un usuario se desconecta.

Veamos la teoría de un chat y comprenderemos rápidamente como funciona FMS en su lado servidor. En una aplicación chat típica, el servidor debe manejar una lista de los usuarios conectados actualmente (los nicks) y debe poder ser capaz de enviar mensajes a todos los usuarios. Por ejemplo, cuando un nuevo usuario se conecta y sin que éste escriba nada, el servidor tiene que poder enviar un mensaje tipo “Nick se ha conectado al chat” a todos los demás usuarios.  Para solucionar todo esto FMS tiene lo que se conocen como objetos compartidos (SharedObjects), objetos a los que todos los clientes pueden acceder.

Si analizas ahora el código de nuestro main.asc verás que el evento onAppStart crea las variables que se utilizarán en la aplicación:

  • userList: array que contendrá la lista de usuarios conectados al sistema.
  • uid:  identificador del último usuario conectado para poder localizar de manera inequívoca a un cliente.
  • users_so: objeto compartido de la lista de usuarios
  • chat_so: objeto compartido del chat propiamente dicho, a él se envíen los mensajes que escriben los usuarios.

Creo que sobran más explicaciones ya que es tan sencillo que se entiende sólo.

Veamos ahora la teoría de qué ocurre cuando se conecta un usuario, evento onConnect. Cada vez que un cliente accede a nuestro chat debemos comprobar, antes de nada, siya hay otro usuario con el mismo nick, en cuyo caso rechazamos su conexión. Si todo va bien y su nick es único debemos guardar al usuario en la lista de usuarios y avisar a todos los clientes ya conectados que hay un nuevo usuario en el chat.

Compara ahora la teoría con el código del evento. El parámetro cliente es automático y representa al cliente que se conecta. Es un objeto con distintos parámetros predefinidos. Los demás parámetros del evento se los pasaremos nosotros desde nuestro cliente flash. Para ver si un nick ya existe simplemente lo comparamos contra todos los demás del array userList que ya hemos visto que contiene a todos los usuarios conectados. El evento onConnect puede aceptar un cliente (acceptConnection) o rechazarlo (rejectConnection).

Si el usuario ha sido admitido, le damos el siguiente UID y tendremos entonces que avisar a los demás usuarios de que tenemos un usuario nuevo. Si recordamos las variables que teníamos, había un objeto compartido al que tienen acceso todos los clientes, users_so, solo tendremos que añadir nuestro nuevo cliente a este objeto:

this.users_so.setProperty(client.uid, client);

Cuando veamos la aplicación cliente veremos como detectamos los cambios en ese objeto, pero es sumamente sencillo.

Espera!. Nos falta enviar el mensaje de “Nick se ha conectado al chat”. Tan sencillo como:

this.chat_so.send("onMsgSend", msg);

Magia!. Como chat_so era un objeto compartido, todos los clientes y el servidor tienen acceso al mismo y lo que uno escriba lo recibirán automáticamente todos los demás. Esta es la verdadera magia de FMS, no tienes que preocuparte de cómo enviar los mensajes a los usuarios uno a uno, se sincronizan sólos.

Como último detalle, cuando aceptamos la conexión de un cliente llamamos remotamente al método onUserid de la aplicación cliente. Si! has leído bien!, FMS llama a una función de tu SWF!. Más adelante veremos para qué, simplemente recuerda que cuando te conectas al chat el servidor llama a un método de la aplicación pasándole su propio UID.

Finalmente veamos el último evento del servidor, bastante obvio si has seguido lo explicado anteriormente y lo has entendido, la desconexión de un usuario.

Sin pensar mucho más entenderemos que habrá que eliminar al cliente de userList y de users_so. Pues eso mismo, no hay mucha más ciencia.

Ya tenemos la parte del servidor terminada. Sencillo ¿no?. Es posible que aún no entiendas algunas cosas. Tranquilo, cuando veamos el cliente lo entenderás todo y verás que realmente es muy sencillo crear aplicaciones de este tipo con FMS.

La consola de administración

Es una aplicación Flash desde la que podemos conectarnos como administradores al servidor FMS y realizar operaciones de mantenimiento, entre ellas cargar y descargar instancias de aplicación, ver el log del sistema (los trace), examinar objetos (en modo debug)…

Cuando instalaste FMS te pidió un usuario y una clave además de un puerto donde instalar esta consola de administración. No tienes más que conectarte a ella y comenzarás a ver estos datos.

Consola de administración de FMS

Hay, sin embargo, algunas particularidades de administración que conviene tener en cuenta. Para habilitar las opciones de debug de objetos habrá que modificar el fichero conf/_defaultRoot_/_defaultVHost_/Application.xml cambiando:

<AllowDebugDefault>false</AllowDebugDefault>

a true. Reinicias el servidor para que el cambio se active y verás algo como esto que te servirá para depurar tu aplicación.

Consola de administración de FMS

En la barra de opciones de aplicación (Live log, clients…) tienes dos pequeños botones que te permitirán recargar una instancia de la aplicación o descargarla por completo, las opciones que comentábamos antes que te ayudarán durante el desarrollo para no tener que reiniciar todo el servidor.

La aplicación cliente

Y llegamos al último punto. La aplicación, el chat, lo que ve el usuario.

Ya os he puesto una captura del resultado final. Veremos ahora los entresijos. Abajo de todo tenéis el código fuente de todo el proyecto de demostración.

Me meteré directamente al meollo de conectar con FMS y enviar y recibir datos, la interfaz de usuario y la pantalla inicial de login la dejo para vosotros. Como veréis hay una clase Chat.as que centraliza casi todo el proceso de comunicación con el servidor.

chat = new Chat();
chat.addEventListener(UserIDEvent.USER_ID, onUID);
chat.addEventListener(NCEvent.STATUS, onStatus);
chat.addEventListener(NCEvent.NONICK, onStatus);

salaSeleccionada=sala.selectedItem.toString();
chat.init("rtmp://"+host+"/textchat/"+salaSeleccionada, StringUtil.trim(nick.text), StringUtil.trim(clave.text), salaSeleccionada);

private function onUID( event:UserIDEvent ):void{
    miuid=event.userid;
    currentState = "chatState";
    msg.setFocus();
    CursorManager.removeBusyCursor();
    this.addEventListener(KeyboardEvent.KEY_UP,keyHandler);
    picker.addEventListener(DropdownEvent.CLOSE, pickerClose);   
}

private function onStatus( event:NCEvent ):void{
    CursorManager.removeBusyCursor();
    if (event.status == "closed"){
        status.text = "Desconexión completa.";
    }
    if (event.status == "nickexiste"){
        status.text = "Usuario o contraseña incorrectos.";
    }
    currentState = "";
}

Al crear la instancia de Chat haremos que se detecten un par de eventos que ejecutarán:

  •  onUID: se dispara cuando el servidor, como hemos visto antes, llama al método cliente onUserid. Nos sirve para cambiar el estado de la interfaz de usuario desde la ventana de login a la de chat ya que sólo si hemos conectado recibiremos el UID.
  • onStatus: nos ayudará adetectar conexiones rechazadas y tu propia desconexión.

Para conectar véis que llamamos al método init de Chat con cuatro parámetros, la url de la instancia de nuestra aplicación (definida como host+aplicacion+sala de chat), el nick, la clave (para un futuro) y la propia sala de chat. Si examinas este método verás qué sencillo es iniciar una conexión. El método más importante es

private function netStatus( event:NetStatusEvent ):void{
    var info:Object = event.info;
    switch(info.code)
    {
    	case "NetConnection.Connect.Success":
    	    users_so = SharedObject.getRemote("users_so", nc.uri, false);
    	    users_so.addEventListener(SyncEvent.SYNC, usersSync);
    	    users_so.addEventListener(NetStatusEvent.NET_STATUS, onSOnetStatus);
    	    users_so.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSOSecurityError);
    	    users_so.client = this;
    	    users_so.connect(nc);
    	    chat_so = SharedObject.getRemote("chat_so", nc.uri, false);
    	    chat_so.addEventListener(NetStatusEvent.NET_STATUS, onSOnetStatus);
    	    chat_so.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSOSecurityError);
    	    chat_so.client = this;
    	    chat_so.connect(nc);
    	    break;
    	case "NetConnection.Connect.Rejected":
    	    dispatchEvent( new NCEvent(NCEvent.NONICK, "nickexiste"));
    	    break;
    	case "NetConnection.Connect.Closed":
    	    dispatchEvent( new NCEvent(NCEvent.STATUS, "closed"));
    	    break;
    }
}

Es el que detecta el estado de la conexión, si el servidor nos acepta o nos rechaza tal como veíamos antes e incluso si nos desconecta. Fíjate que si somos aceptados estaremos conectándonos a los SharedObjects que creamos antes en el servidor 😉 . Ahora es Chat quien hará absolutamente todo el trabajo, la aplicacion mxml es la interface de usuario que transmite a Chat los comandos necesarios. Veamos por ejemplo como indicamos al chat que envíe a los demás usuarios un mensaje que hayamos escrito nosotros.

private function sendMsg():void{
    if ( msg.text.length > 0 ){
        chat.sendMsg(msg.text);
        msg.text = "";
    }
}

Vale sí, pero has hecho trampas, aquí estás dejando el meollo en manos de Chat 😛 . Bien, veamos qué ocurre en sendMsg.

public function sendMsg( color:String, msg:String ):void{    
    var outmsg:String = "";    
    outmsg = "<font color='#" + color + "'><b>" + this.username+":</b> " + msg + "</font><br>";
    chat_so.send("onMsgSend", outmsg);
}

¡ Sólo eso ! Como ya estamos conectados al SharedObject chat_so, le decimos a éste que llame al método onMsgSend de sus clientes y le pase el texto que hemos escrito.

De acuerdo, entonces ¿cómo leemos los mensajes que escriben los demás usuarios?. Te lo acabo de decir 😛 . Cuando un usuario escribe algo, llama (da igual cómo) a un método local de cada usuario que se encarga de escribir el texto en su ventana de chat.

public function onMsgSend( msg:String ):void{
    chatHistory += msg;        
    dispatchEvent(new Event("histChange"));
}

Obviamente este método tiene que ser público. Al despachar ese evento simplemente hace que se actualice el texto del textarea.

Hasta ahora creo que lo voy entendiendo, pero no has explicado como detectamos las entradas y salidas de usuarios. Cierto, veámoslo pues. Acabamos de ver que cuando entramos al chat y somos admitidos conectamos con los SharedObjects que creamos en el servidor. Si te has fijado (y si no hazlo ahora), al de usuarios le poníamos un listener distinto:

users_so.addEventListener(SyncEvent.SYNC, usersSync);

Esto significa que cada vez que haya un cambio en el objeto users_so todos los clientes serán notificados de ello. Por ejemplo, cada vez que entra o sale un usuario vimos que añadíamos o quitábamos un elemento de este objeto que representaba a ése usuario. Cada una de estas operaciones hará que nuestra aplicación cliente reciba una llamada a usersSync.

private function usersSync(event:SyncEvent):void{
    var results:Object = event.target.data;
    var usersArray:Array = new Array();
    var existe:Boolean=false;
    //debo actualiza solo los datos
    for( var i:String in results ){
        if(results[i].sala==this.sala){
            existe=false;
            for(var j:Number=0; j<dpUsers.length; j++){
                if(dpUsers[j].uid==results[i].uid){
                    existe=true;
                    break;
                }
            }

            if(existe){
                //por si se ha cambiado el nick
                dpUsers[j].nick=results[i].nick;
            }else{
                var clientObj:Object=new Object();   
                clientObj.nick = results[i].nick;
                clientObj.uid = results[i].uid;
                dpUsers.addItem(clientObj);
            }
        }
    }
    //ahora tengo que eliminar las bajas
    //elimino empezando por la ultima para no cambiar los indices
    for(j=dpUsers.length-1; j>=0; j--){
        existe=false;
        for(i in results ){
            if(dpUsers[j].uid==results[i].uid && results[i].sala==this.sala){
                existe=true;
            }
        }

        if(!existe){
            dpUsers.removeItemAt(j);
        }
    }
    //finalmente ordenamos los usuarios
    var sort:Sort = new Sort();
    sort.fields = [new SortField("nick",true)];
    dpUsers.sort = sort;
    dpUsers.refresh();
    dispatchEvent(new Event("dpChange"));           
}

Esta método repasará todos los usuarios del SharedObject comparándolos con los de la lista de usuarios del canal donde nos encontramos chateando y añadiendo los nuevos usuarios y eliminando los que hayan salido.  Quizás no sea el mejor método para manejar los usuarios y habría sido mucho más sencillo tener una pareja de métodos addUsuario y delUsuario que manejen todo automáticamente con llamadas remotas igual que con los mensajes, pero quería que viésemos el modo de recibir avisos de cambios en un SharedObject.

Conclusiones

Hemos visto cómo de una manera sencillísima podemos crear nuestro propio chat sin excesivas complicaciones. Piensa además que esto es, seguramente, lo más sencillo, el primer paso con FMS, las posibilidades reales son infinitas.

Quizás en otros artículos ampliemos las posibilidades de este pequeño ejemplo añadiendo vídeo y audio o, incluso, hagamos un pequeño sistema de grabación de vídeo con tu webcam similar al de Youtube.

Como siempre, aquí os dejo el código completo del proyecto. Recuerda que es un ejemplo de lo que se puede hacer, no debes usarlo tal cual sino aplicar lo que hemos explicado. Del mismo modo habrá cosas que no sean perfectas y se puedan hacer mejor, no lo dudo, mi intención era explicar los conceptos básicos de FMS y que se vea su potencial.

Actualización del cliente IRC online flash, versión 1.0

Hace unos meses anunciábamos el lanzamiento de un cliente IRC realizado en Flash (Flex para ser más exactos) en colaboración con Irc-Hispano. Bien, pues no nos hemos quedado ahí. Durante los últimos meses hemos estado haciendo ajustes y mejoras en la aplicación. Se han solucionado algunos errores que fueron apareciendo y añadido algunas funcionalidades nuevas y llamativas.

El sistema lleva ahora mismo cuatro meses online. En julio hubo 4.500 usuarios únicos diarios de media mientras que en agosto se sobrepasaron los 5.000. Ahora mismo estamos por encima de esa cifra, creo que es un dato más que excelente y más si se compara con los que había antes de su implantación.

Coincidiendo con esta actualización hemos decidido etiquetarla como v.1.0 pues ya es completa, funcional y estable. Hasta ahora estábamos en R.C., hasta tres R.C. pasaron por las manos de los usuarios.

Han sido muchos meses de duro trabajo, no sólo de desarrollo puro y duro, sino además de documentación y planificación. EL RFC del protocolo del IRC ha sido nuestra principal compañía durante mucho tiempo.

Irc Online con fotos

Pero sin duda lo más revolucionario que se ha hecho es permitir a los usuarios poner fotos instantáneas al más puro estilo Messenger. Desde ahora los usuarios ya no tienen que salir del IRC para mostrarse fotos entre ellos. Las fotos, para respetar la intimidad y privacidad de los usuarios, son independientes entre distintas conversaciones, puedes mostrar tu foto a un usuario y no a los demás e incluso tener cada usuario con una foto distinta. Nunca entendimos que a nadie se le haya ocurrido antes permitir a los usuarios verse, a fin de cuentas todos sabemos que es la finalidad de un chat. ¿Qué será lo siguiente? ¿Quizás vídeo? 😉 .

El único inconveniente que tiene el sistema de fotos online es que, por el momento,  solamente lo pueden utilizar aquellos usuarios del webchat y, aunque cada día son más, no cabe duda que el grueso de la gente que chatea utiliza software especializado como mirc y el IRcap.

¿Alguien se atreve con un plugin para mirc para las fotos instantáneas?

Insertar imágenes en RichTextEditor de Flex

Esta semana, a raíz de un mensaje en la lista de correo de Made in Flex, recordé una de las primeras aplicaciones que hice en Flex hace ya dos años con lo cual me lancé a recuperar aquél código, limpiarlo de cosas supérfluas y publicarlo. Os dejo también el enlace a un post mio en la lista Flexcoders donde explicaba como hacerlo, de hecho ha sido mucha la gente que, a partir de ese post, me ha escrito preguntando detalles.

Para los puristas, os aviso de antemano que puede haber cosas que no funcionen bien, código extraño y mil historias más, es un código que tiene dos años, si hacéis cuentas veréis que justo estos días Flex2 cumple dos años. Sí, lo comencé con las primeras betas de aquella versión. Lo único que he hecho ha sido crearlo en Flex3 y dejar la aplicación limpia para que se vea el funcionamiento. Si me váis a dejar un comentario diciendo que tal o cual cosa no funciona bien, podéis ahorrárosla.

Como una imagen vale más que mil palabras, aquí tienes el ejemplo y el código fuente de todo el proyecto.

Todos a los que en algún momento se nos ocurrió utilizar Flex para crear un gestor de contenidos nos hemos dado con un grave problema en la frente: el componente ideal para escribir y actualizar contenidos, RichTextEditor, NO permite, por defecto, insertar imágenes, con lo cual pierde prácticamente su utilidad. Escarbando en la ayuda, sin embargo, te das cuenta de que entre los tag HTML soportados por el componente Textarea se encuentra <IMG>, con lo que, a priori, nada impediría insertar una imagen. En efecto así es y en eso se basa todo este artículo/proyecto. Un detalle importante es que la utilización de imágenes no está bien conseguida en el propio Flash Player, con lo que si comenzamos a añadir y quitar imágenes llegará un momento en el que todo el Textarea será inestable y hará cosas extrañas.

El proyecto se basa en dos componentes, nuestro editor de texto y un explorador de archivos. Desde el editor tendremos un botón Insertar imagen que abrirá un explorador al más puro estilo del escritorio del sistema operativo donde nos mostrará los archivos que tenemos en el servidor, pudiendo subir y eliminar. La lógica del explorador con el servidor la he eliminado al máximo, dejando sólamente unos XML estáticos que listan las carpetas e imágenes disponibles. Para hacerlo bien haríais un script con salida similar a esta pero que liste lo que en realidad hubiese en una ruta de tu servidor.

Nuestro editor de texto

Intentaré explicar como hice todo el proceso, pero fue hace bastante tiempo así que es posible que se me olvide algún detalle.

La idea era hacer un editor avanzado, con las caracteristicas que le faltaban al RichTextEditor original:

  • Insertar imágenes.
  • Posibilidad de añadir datos tabulados (que no tablas como tales).
  • Cambiar el color de fondo del editor para, por ejemplo, crear texto en color blanco.
  • Eliminar todo el texto (e imágenes).
  • Editor avanzado de links.
  • Botón guardar texto (lo insertaría en tu base de datos)

Partimos para ello de un RichTextEditor al que le quitamos el botón predefinido de Añadir link y añadimos nuestros nuevos botones. Siguiendo el código entenderás lo que hacen, por lo que nos centraremos en el de imágenes que es el objeto del artículo. Primero os explico en qué consiste el botón de tabular datos. Es muy sencillo. Si quisiésemos, en el RichTextEditor, crear una tabla de valores con el tabulador, no podríamos, ya que al presionar tabulador saldría el foco del Textarea de escribir  y se iría, por defecto, al selector de fuente. Para solucionarlo añadimos un botón que lo único que hace es añadir un carácter de tabulación (t) en el texto, con lo que comprobaréis que ya puedes tabular datos perfectamente. Muy útil el truco.

Pasemos pues al botón de insertar imágenes. El botón, tal y como expliqué anteriormente, abre el popup del explorador de archivos, con lo que esta parte la veremos en la siguiente sección. Cuando en el explorador seleccionamos la imagen a insertar se devuelve el control al editor y es éste el que añade la imagen. Para añadir la imagen inserta en la posición del cursor el tag <IMG> con los parámetros necesarios. Aparentemente no tiene más truco, pero al empezar a probar cosas nos damos cuenta que sí que lo tiene.

Para empezar, si guardamos el htmltext de este RichTextEditor en la base de datos y posteriormente intentamos recuperarlo recibiremos un desagradable error de validación XML. Quiero aclarar antes de nada, que este error me ocurría en aquél entonces, quizás, ojalá, las ultimas versiones de Flex lo hayan solucionado, te ahorraría muchos problemas. La causa del error de validación era que internamente, aunque tu añadieses un tag <img … /> (o <img…></img>) válido, el editor te devolvía siempre <img .. > , es decir, el XML sin cerrar, con lo cual al cargarlo de nuevo saltaba error de validación. Para solucionar este problema creamos un método desProcesaTexto que lo que hace es convertir todos los tags <img..> no válidos a tag válidos, con lo que tenemos el primer problema resuelto. Utilizando expresiones regulares será extremadamente sencillo.

var pattern:RegExp = /<IMG([^>]*)>/gi;
texto=texto.replace(pattern, "<IMG $1 ></IMG>");

Segundo problema. Una vez insertamos una imagen, ¿cómo podemos quitarla o modificarla? Aquí viene parte del truco. La idea, básicamente, es añadir a cada imagen un link de manera que al hacer click en ella se nos abra un popup que nos permita eliminar la imagen o modificar sus atributos. Esto genera otro inconveniente. Cuando queremos recuperar el contenido del Textarea para guardarlo en la base de datos debemos eliminar estos links falsos añadidos para la interfaz de usuario, pero que no queremos que salgan cuando se muestre el texto. Por contra, cuando cargamos un texto desde la base de datos debemos procesar los atributos de imágenes para añadirles este link y podamos operar con ellos.

var temp:XML=XML("<texto>"+texto+"</texto>");
var allTags: XMLList;
var item:XML;
var tempitem:XML;allTags= temp..IMG;
for each(item in allTags) {
    var xlcParent:XMLListCollection = new XMLListCollection(item.parent().parent().children());
    tempitem=XML(xlcParent.toXMLString());
    xlcParent.setItemAt(item, 0);
}

Como se aprecia en el código, bucamos todos los tags <IMG> y, asumiendo que todos tendrán un tag <A> anterior, reemplazamos el nodo <A> completo por el <IMG> y todo solucionado, más sencillo de lo que parece una vez se entiende el procedimiento.

Para contemplar estas peculiaridades cada vez que asignamos el texto al componente o cada vez que lo recuperamos, tenemos los siguientes métodos:

public function getHtmlText():String{
    return desProcesaTexto(this.htmlText);
}

public function setHtmlText(texto:String):void{
    this.htmlText=procesaTexto(texto);
}

No hay mucho más que explicar. Como peculiaridad, veremos como procesaríamos los tags <IMG> cuando cargamos un texto nuevo en el componente. La idea, básicamente, es añadir el link que nos permita modificarla, pero este link necesita conocer los datos de la imagen (src, width, height..).

allTags= temp..IMG;
for each(item in allTags) {
    var xlcParent:XMLListCollection = new XMLListCollection(item.parent().children());
    var iIndex:int = xlcParent.getItemIndex(item);    idlink=Math.round((Math.random()*100000)*(Math.random()*100000));
    nuevoNodo="<A href="event:IMAGEN##||##"+item.@ID+"##||##"+item.@SRC+"##||##"+item.@WIDTH +
    "##||##"+item.@HEIGHT+"##||##"+item.@VSPACE+"##||##"+item.@HSPACE+"##||##"+item.@ALIGN +
    "##||##"+idlink.toString()+"" ID=""+idlink.toString()+"">"+xlcParent.toXMLString()+"</A>";

    xlcParent.setItemAt(XML(nuevoNodo), iIndex);
}

Sencillo.

El explorador de archivos

El explorador de archivos es un componente bastante resultón que, si lo trabajas un poco, te puede solucionar muchas tareas. Igual que el Explorador de Archivos de Windows, tienes a la izquierda un árbol de directorios y a la derecha los archivos de la carpeta seleccionada y sus detalles. Arriba tenemos botones para subir archivos nuevos y crear nuevas carpetas (la lógica de estos botones es cosa tuya) y, sobre todo, un combo para cambiar el modo de ver los archivos de manera que puedes ver la lista de los mismos o las miniaturas. Si, si listas imágenes verás un thumbnail de las mismas, perfecto para nuestro editor. No voy a enrollarme mucho más con el funcionamiento ya que en el código fuente tienes todo lo necesario. El evento interesante es el doble click sobre un archivo, que devuelve el control a una nueva ventana donde puede configurar los parametros de la imagen a insertar. Con el ejemplo lo entenderas.

En tu editor deberás crear los métodos adecuados en el servidor para listar las carpetas y archivos de tu directorio de uploads si quieres que todo sea dinámico y el usuario pueda organizarse por su cuenta los archivos.

Tal como había comentado, no he sido tan explícito como en otras ocasiones. Si tienes algún problema no dudéis en dejar un comentario e intentaré solucionarlo a la mayor brevedad posible.

Aquí tienes el ejemplo y el código fuente de todo el proyecto.

Cliente IRC online en Flex en colaboración con Irc-Hispano

Ayer lunes entró en producción uno de nuestros proyectos más ambiciosos junto a Irc-Hispano, la red de chat más grande e importante del mundo hispanohablante. Se puede acceder a nuestra aplicación desde aquí.

Cliente IRC Flash

Hacía muchos años que queríamos algo así pero no existía la tecnología necesaria. Los que usamos Internet desde hace muchos años, en mi caso desde 1994, sabemos que antes de que existiese el Messenger ya estaba el IRC, el chat de toda la vida. El problema era que se necesitaban programas concretos para utilizarlo (Mirc) y después había que saber configurarlo y tener unas nociones básicas (servidores, nicks, canales, kick, ban, join…). Muy complicado para el usuario no experto. Así apareció a finales de los 90 la probablemente mayor utilidad de los applets de Java: el cliente IRC online. Ahora los usuarios ya no necesitaban conocimientos ni instalarse nada, simplemente accedían a la web, ponian su nick, el canal donde querían chatear, y a “relacionarse”. Incluso aparecieron clientes IRC en HTML, muy útiles pero sin las opciones de usabilidad de las otras.

La idea era muy buena, pero nadie había contado con los problemas asociados: necesidad de la máquina virtual de Java, lentitud, pesadez, etc. Los usuarios no tenían más que problemas.

Corría entonces el año 2002 cuando se nos ocurre hacer lo mismo pero en Flash, una tecnología presente en la mayoría de navegadores y con una implantación mucho mayor que la de Java. Nos pusimos a investigar y nos llevamos una gran decepción, no había forma de comunicarse con un servidor IRC estandar, en aquél momento Flash sólo disponía de xmlsocket, que permitía conectarse a hosts remotos pero siguiendo unas especificaciones especiales.

Todo estaba perdido hasta 2006. En junio de este año Adobe (que ya había absorvido a Macromedia) lanzaba Flex2 y junto a él la versión 9 de Flash Player con la funcionalidad más esperada por nosotros: la posibilidad de crear sockets binarios. Con esto ya se podían hacer en Flash clientes pop/smtp, ftp y, por supuesto, IRC. Esto marcó un antes y un después y nos pusimos de inmediato a planificar nuestro sueño. Tardamos más de seis meses en meternos de lleno en el proyecto puesto que ya estábamos trabajando en otros.

Desde el primer día tuvimos que comenzar a pelearnos con el RFC del protocolo IRC, fundamental para conocer el funcionamiento del sistema ya la sintaxis de todos los mensajes de ida y vuelta con el servidor. Una vez tuvimos el núcleo básico fue relativamente sencillo construir toda la interfaz de usuario sobre él, creando los comandos y acciones así como los eventos de respuesta. El primer problema era que había muchas opciones distintas, así que fuimos fijando prioridades y trabajando sobre ellas.

Cuando teníamos una versión básica pero funcional y debido a las políticas de seguridad de la máquina virtual Flash, nos pusimos en contacto con Irc-Hispano y desde el primer momento les encantó la idea, los planes y nuestro prototipo, con lo que fue sencillo llegar a un acuerdo que beneficiase a ámbas partes. Mientras nosotros desarrollábamos, la gente de Irc-Hispano se ocupaba del testing.

Hablando ya técnicamente, y aunque esté mal que yo lo diga, se ha hecho un trabajo impresionante exprimiendo toda la potencia de Flex. Hemos conseguido integrar bastantes cosas que a simple vista son casi imposibles de utilizar en Flex, como los smileys en un área de texto, o los colores de fondo. Documentándonos por aquí y por allí y viendo lo que habían conseguido otros conseguimos adaptar ciertos sistemas a nuestras necesidades quedando el conjunto francamente bien.

Desde el primer momento tuvimos claro que la única forma de conseguir sacar una aplicación de este tipo era usando técnicas de desarrollo ágiles, y así lo hicimos, preparando periódicamente versiones funcionales de lo que hubiese y poniéndola a disposición de los usuarios para que nos aportasen el feedback necesario, no solo de errores sino también de usabilidad y funcionalidad en general. La experiencia ha sido perfecta y todo el equipo ha salido beneficiado de este modo de trabajo, ya que se elimina automáticamente el estrés del miedo a los cambios cuando el producto está ya terminado.

Y así llegamos hasta hoy en que se lanza públicamente. Se han hecho muchas más funcionalidades de las previstas inicialmente y seguramente se irán haciendo muchas más a medida que se vaya utilizando. Por nuestra parte ha sido un esfuerzo enorme en horas de trabajo y quebraderos de cabeza para hacer y solucionar problemas sin respuesta aparente, pero el resultado ha valido la pena.

A partir de ahora esperamos ir corrigiendo bugs y añadiendo mejoras, ideas no nos faltan y seguramente habrá muchas sorpresas ;). Algún día también refactorizaremos :P.

Domina rápidamente Adobe Air

Leo en el blog de Mario Casario esta entrada donde anuncia la disponibilidad gratuita y bajo licencia Creative Commons de la guía Adobe AIR 1 for JavaScript Developer en formato PDF.

Para los que no lo sepáis, Air es la tecnología de Adobe para desarrollar rápidamente aplicaciones de escritorio utilizando otras tecnologías ya existentes: HTML, Javascript y Flash. Así de sencillo, aplicando lo que ya sabes puedes generar aplicaciones de escritorio multiplataforma (Windows y Mac ahora mismo, Linux en camino).

Air está de moda y más desde la compra de Twhirl por parte de Seesmic, de hecho Twitter es de lo más utilizado para crear aplicaciones Air.

Con Air puedes crear rápidamente pequeñas aplicaciones con acceso al sistema de archivos local, bases de datos locales (SQLlite), arrastrar y soltar… todo lo que necesitas para crear tus aplicaciones.

Os aseguro que es increíble lo que se puede hacer con poquísimas líneas de código.

Flash Player, sockets y políticas de seguridad

El pasado miércoles 9 de abril Adobe liberó una actualización de su Flash Player numerada como 9.0.124. Esta noticia pasaría completamente desapercibida si no fuese por las implicaciones que tiene. De hecho no entiendo como una actualización con los cambios que conlleva se ha numerado como una actualización menor y no se le ha dado más importancia.

El cambio fundamental de esta nueva versión radica en el modelo seguridad de la máquina virtual Flash al de acceder a sockets remotos y al utilizar headers HTTP. Recordemos que la opción de abrir sockets binarios y conectarse a ellos permitiendo aplicaciones bajo cualquier protocolo apareció en julio de 2006 junto a la versión 9 de Flash Player. Hasta ahora el modelo de seguridad de sockets era el mismo que para cualquier llamada remota bajo Flash, el famoso crossdomain.xml. Según esto, para acceder a cualquier archivo que no resida bajo el mismo host que sirve el swf que hace la llamada, el host servidor debe autorizar a la maquina virtual Flash la carga de ese archivo a través de ese crossdomain.xml, si no existe o no se autoriza el acceso, no se podrá acceder. Este es, por tanto, un archivo de políticas de seguridad y decide quién puede conectarse con Flash a tu servidor. A mi, personalmente, siempre me ha parecido algo ridículo, es como si para cargar un archivo de cualquier host desde tu navegador tuviesen que autorizártelo, pero siempre habrá quien encuentre ventajas en la seguridad. Si el archivo está publicado en Internet se supone que ya estas autorizado a leerlo, ¿para qué complicar más todo el proceso requiriendo más políticas de seguridad?. Y vaya si se complica…

Hasta ahora los sockets se trataban como archivos en lo que a políticas de seguridad se refiere, es decir, el crossdomain.xml del host al que conectábamos nos autorizaba el acceso igual que con cualquier llamada http. Si tienes una aplicación que utiliza sockets y actualizas a Flash Player 9.0.124 verás que ahora no te funciona, te salta una excepción de seguridad. Los chicos de Adobe se han inventado un sistema de políticas de seguridad exclusivo para sockets e independiente de las llamadas http.

Desde Adobe se han currado una ayuda/explicación de 7 páginas. Ninguna pega a no ser porque llegas al final de la lectura con la pregunta, vale, pero ¿qué tengo que hacer para que funcione? Es decir, mucha lectura pero poca, muy poca ayuda, da la impresión de que repiten lo mismo en todas las páginas sin aclarar nada.

La solución es sencilla a la par que curiosa y complicada para muchas aplicaciones. Debes crear una pequeña aplicación que, bajo el puerto 843 por defecto (aunque puedes usar otros puertos), debe responder a las peticiones de acceso con el crossdomain.xml. La mayoría de la gente ha entendido que debes tener tu servidor web a la escucha en el puerto 843 y devolver el crossomain.xml desde allí (como veis no está clara la documentación), pero NO es eso, no es una llamada HTTP la que te va a hacer el swf para pedir autorización. Veámoslo con un ejemplo práctico. Supongamos que fuese una solicitud HTTP, a grosso modo haríamos:

telnet www.tuhost.com 843
GET /crossdomain.xml HTP/1.0
HTTP/1.x 200 OK
Date: Tue, 15 Apr 2008 17:24:26 GMT
Server: Apache
Last-Modified: Thu, 28 Oct 2004 21:57:25 GMT
Content-Length: 128
Content-Type: text/xml

<xml version="1.0" encoding="UTF-8">.....</xml>

El xml final sería nuestro crossdomain.xml. Esto sería lo mismo que si cargamos el crossdomain.xml desde el puerto 80. Vale, pues esto es lo que NO va a hacer el swf, por tanto, no sirve. Esto es lo que va a hacer:

telnet www.tuhost.com 843
<policy-file-request/>
<xml version="1.0" encoding="UTF-8">.....</xml>

La diferencia es evidente, en el segundo caso, al contectar con el servidor simplemente le decimos <policy-file-request/> y nos debe devolver el xml del crossdomain.xml. No es el protocolo HTTP, es uno inventado que sólo responde a una petición.

El puerto no tiene porqué ser el 843, puedes modificarlo, pero Adobe pretende que 843 sea el estándar y, de hecho, está solicitando su homologación con el IANA.

Desde Adobe incluso se han puesto cachondos y te dicen que lo ideal es modificar tu aplicación en el servidor para que acepte la llamada <policy-file-request/> y responda con el XML, así no necesitarás una aplicación adicional. Pues nada chicos, a modificar todos los servidores smtp, pop, irc (nuestro caso), ftp… para que acepten la llamada. ¡Qué ridículo!

Como soy un tío enrollado os paso la solución completa. En esta URL tenéis un ejemplo de servidor en TCL/TK que funciona a la perfección y no consume recursos. Configura el puerto y la ruta al crossdomain.xml y a funcionar. Recuerda que si el puerto es inferior al 1024 deberá ser un usuario privilegiado quién ejecute la aplicación, deberías, entonces, enjaularla (chroot) para evitar posibles puertas. En realidad lo que hacemos es responder con el crossdomain.xml a cualquier petición que nos hagan, sea la correcta o no, ¿qué mas da? No vamos a hacer absolutamente nada más con ese puerto. Tendréis que preocuparos también de que se quede residente como demonio en vuestro servidor y, además, habrá que comprobar periódicamente que sigue a la escucha. Para esto recomiendo hacer un script que haga llamadas reales y espere el código XML necesario, comprobar simplemente con un netstat que el servidor ocupa el puerto no nos salvará de que el servidor se quede zombie. Por supuesto no olvides abrir tu firewall a ese puerto.

Actualización.
Acabo de terminar el artículo y a través de flexcoders me llega este enlace, mucho más claro y sencillo que la ayuda inicial. Ah! y con ejemplos de servidores en Perl y Python. En líneas generals viene a contar lo mismo que acabo de contar yo :P.