Diferencia entre revisiones de «Empezar con jaxmpp2»

De ChuWiki
Saltar a: navegación, buscar
m
m (Establecer la conexión)
 
Línea 73: Línea 73:
 
El usuario será usuario@openfire. En la instalación y configuración de openfire, hay un campo para indicar cual es el nombre del servidor, que puede o no ser el de la URL (es un campo interno de openfire). En el nombre de usuario aparece @openfire y este debe ser el valor de ese campo interno de openfire, que en nuestro caso coincide con el que hemos dado al servidor. Dentro de la consola de administración de openfire (http://openfire:9090), se puede configurar este nombre en "servidor"->"Administración del servidor"->"Configuración del servidor"->"Editar propiedades".
 
El usuario será usuario@openfire. En la instalación y configuración de openfire, hay un campo para indicar cual es el nombre del servidor, que puede o no ser el de la URL (es un campo interno de openfire). En el nombre de usuario aparece @openfire y este debe ser el valor de ese campo interno de openfire, que en nuestro caso coincide con el que hemos dado al servidor. Dentro de la consola de administración de openfire (http://openfire:9090), se puede configurar este nombre en "servidor"->"Administración del servidor"->"Configuración del servidor"->"Editar propiedades".
  
 +
Si el servidor de openfire lo permite, podemos establecer una conexión anónima, sin usuario y password. Para que funcione correctamente, no indicamos usuario y password, pero sí debemos indicar el nombre del servidor
 +
 +
<pre>
 +
conexion.getProperties().setUserProperty(SessionObject.SERVER_NAME,"openfire");
 +
conexion.getProperties().setUserProperty(AbstractBoshConnector.BOSH_SERVICE_URL_KEY, URL_OPENFIRE);
 +
</pre>
 +
 +
donde "openfire" es el nombre del servidor de openfire, tal cual se pondría como segunda parte del nombre de usuario usuario@openfire.
  
 
== Obtener las conferencias ==
 
== Obtener las conferencias ==

Revisión actual del 10:58 3 may 2011

Introducción

jaxmpp2 es una librería GWT que nos permite hacer un cliente web de chat usando GWT y con el protocolo XMPP (en el que se basa jabber). Puesto que la documentación de esta librería es bastante escasa, en este tutorial vamos a ver un pequeño ejemplo sobre cómo empezar con ella, lo básico para entrar en una sala de multiconferencia.

Las librerías

Lo primero que necesitamos son las librerías jaxmpp2. Podemos bajarlas de subversion desde https://svn.tigase.org/reps/jaxmpp2 . En ese repositorio hay tres librerías, sólo necesitamos jaxmpp-core y jaxmpp-gwt. Pero si queremos utilizar maven, necesitaremos sacar los tres proyectos y el pom.xml principal.

Una vez extraidas las dos librerías, sólo necesitamos generar para cada una de ellas un jar que contenga los fuentes, los .class y el fichero gwt.xml que hay en los directorios correspondientes. Si hemos extraído todo el proyecto y usamos maven, nos bastará con ejecutar el comando "mvn install" en el directorio principal para que los jar queden en nuestro repositorio de maven, o bien ejecutar mvn package y luego recoger los jar de los directorios target correspondientes

./jaxmpp-core/target/jaxmpp-core-2.0.0-SNAPSHOT.jar
./jaxmpp-gwt/target/jaxmpp-gwt-2.0.0-SNAPSHOT.jar

y añadirlas a nuestro proyecto gwt.

Los servidores

Como servidor de chat puede valernos, por ejemplo, openfire, que se instala fácilmente. Openfire atiende por defecto a clientes http por el puerto 7070.

Nuestro cliente, aunque lo hagamos con java, GWT lo traducirá a unas páginas html con javascript, por lo que usaremos también un servidor apache para alojar esas páginas.

Tener que usar dos puertos en el mismo servidor (el 7070 de openfire y el 80 de Apache), nos puede dar dos problemas:

  • El puerto 7070 puede estar bloqueado por los proxys, cortafuegos, routers, etc. No así el puerto 80, que al ser el estándar para navegar por internet, suele estar abierto.
  • Al tener una aplicación (nuestro chat) que intenta acceder simultáneamente a dos servidores en puertos distintos (Apache en el 80 y Openfire en el 7070), podemos tener problemas con nuestro navegador. Por seguridad, los navegadores no suelen permitir en determinadas condiciones que una página se componga usando javascript accediendo a la vez a dos servidores (ver cross site scripting). Eso hará que nuestra aplicación no funcione correctamente al no tener los permisos adecuados.

Para evitar ambos problemas, es necesario hacer una redirección en este servidor apache, de forma que nuestra aplicación hablará con el chat a través de una URL normal del apache (por ejemplo, http://servidor/http-bind/) y será apache el que redirija estas peticiones al servidor openfire, puerto 7070. Esta redirección se hace con un virtual host de apache. En nuestro ejemplo, la definición del virtual host será la siguiente

<VirtualHost *:80>
  ServerName servidor
  DocumentRoot "C:/wamp/www/"
  RewriteEngine on
  RewriteRule /http-bind http://openfire:7070/http-bind/ [P]
</VirtualHost>

con esto, cualquier llamada a http://servidor/http-bind se redirigirá a http://openfire:7070/http-bind/. En cuanto a los nombres de los servidores, hay un pequeño lío, vamos a tratar de aclararlo. Hay varios nombres implicados

  • Por un lado, el nombre con el que accedemos a nuestro servidor apache, es el nombre del dominio http://servidor
  • Por otro lado, el nombre del servidor o dominio donde corre nuestro servidor openfire. Puede ser el mismo que el de apache u otro. http://openfire:7070
  • El nombre ServerName que ponemos en el VirtualHost. Podemos poner el nombre que queramos mientras funcione la redirección, así que ponemos el mismo nombre que el dominio del servidor apache.

Esta redirección hace difícil que podamos ejecutar nuestro programa java con GWT en nuestro entorno de trabajo favorito (eclipse, netbeans), ya que en el servidor interno de estos IDEs no podremos hacer estas redirecciones.

Establecer la conexión

Lo primero de todo es establecer la conexión con el servidor. El trozo de código, usando jaxmpp2 será este

private static final String URL_OPENFIRE = "http://servidor/http-bind/";
private static final String PASSWORD = "password";
private static final String USUARIO = "usuario@openfire";

Jaxmpp conexion = new Jaxmpp();

conexion.getProperties().setUserProperty(SessionObject.USER_JID,
				JID.jidInstance(USUARIO));
conexion.getProperties().setUserProperty(SessionObject.PASSWORD,
				PASSWORD);
conexion.getProperties().setUserProperty(AbstractBoshConnector.BOSH_SERVICE_URL_KEY,
				URL_OPENFIRE);

try {
	conexion.login();
} catch (JaxmppException e) {
	// Error en la conexión
}

La URL de openfire será http://servidor/http-bind/, que cómo hemos visto, será redirigida al puerto 7070 de openfire. Es importante, para evitar problemas de cross-site-scripting que la base de esta URL sea la misma que la del servidor Apache donde se aloja nuestra aplicación (http://servidor en este ejemplo).

El usuario será usuario@openfire. En la instalación y configuración de openfire, hay un campo para indicar cual es el nombre del servidor, que puede o no ser el de la URL (es un campo interno de openfire). En el nombre de usuario aparece @openfire y este debe ser el valor de ese campo interno de openfire, que en nuestro caso coincide con el que hemos dado al servidor. Dentro de la consola de administración de openfire (http://openfire:9090), se puede configurar este nombre en "servidor"->"Administración del servidor"->"Configuración del servidor"->"Editar propiedades".

Si el servidor de openfire lo permite, podemos establecer una conexión anónima, sin usuario y password. Para que funcione correctamente, no indicamos usuario y password, pero sí debemos indicar el nombre del servidor

conexion.getProperties().setUserProperty(SessionObject.SERVER_NAME,"openfire");
conexion.getProperties().setUserProperty(AbstractBoshConnector.BOSH_SERVICE_URL_KEY, URL_OPENFIRE);

donde "openfire" es el nombre del servidor de openfire, tal cual se pondría como segunda parte del nombre de usuario usuario@openfire.

Obtener las conferencias

El servidor tiene un servidor de conferencias (en nuestro caso sería conference.openfire porque lo tenemos así configurado en el servidor de openfire). Este servidor de conferencias puede tener muchas salas dentro, públicas o privadas, donde los usuarios chatean.

El código para acceder a este servidor de conferencias donde puede haber muchas salas es el siguiente.

MucModule multiChat = conexion.getModulesManager().getModule(MucModule.class);

Es en la clase MucModule así obtenida donde debemos suscribirnos para informarnos de todo lo que ocurre en las salas: textos que se están escribiendo y usuarios que entran y salen de las salas.

Suscribirse a las conferencias

Ahora debemos hacer varias cosas. Por un lado, suscribirnos a los mensajes que lleguen para poder sacarlos por pantalla.

multiChat.addListener(MucModule.MessageReceived,
   new Listener<MucEvent>() {
      public void handleEvent (MucEvent e) {
         try {
            if (null == e.getMessage().getBody()) {
               // No es un mensaje visible
               return;
            }
            // e.getNickname() contiene el alias del que ha escrito el texto recibido
            // e.getMessage().getBody() cotiene el texto escrito.
            // e.getRoom() devuelve la sala concreta en la que se ha escrito ese texto.					
         } catch (XMLException e1) {
            // Se ha producido algún error.
         }
      }
   });

Por otro lado, suscribirnos a cuando un ocupante entra o deja la sala, por si queremos mostrar cuando un usuario sale o entra, o por si queremos llevar una lista aparte con los ocupantes de la sala. El código puede ser este

      multiChat.addListener(MucModule.OccupantComes, new Listener<MucEvent>() {
         @Override
         public void handleEvent(MucEvent be) {
            try {
               // be.getNickname() entra en be.getRoom()
            } catch (XMLException e) {
               // Algún error
            }
         }
      });
      multiChat.addListener(MucModule.OccupantLeaved, new Listener<MucEvent>() {
         @Override
         public void handleEvent(MucEvent be) {
            try {
               // be.getNickname() sale de be.getRoom()
            } catch (XMLException e) {
               // Algún error.
            }
         }
      });

Obtener la sala concreta

El código anterior es para el servidor de conferencias y enterarnos de lo que sucede en las salas. Si queremos unirnos a una sala concreta para chatear en ella y obtener los ocupantes de la misma, el código es el siguiente

private static final String CONFERENCIAS = "conference.openfire";

Room habitacion = multiChat.join("sala", CONFERENCIAS, "alias");

donde "conference.openfire" es el servidor de conferencias, cuyo nombre se configura en el panel de administración de openfire. "sala" es el nombre de la sala, que debe también estar creada desde el panel de administración de openfire y nuestro usuario debe tener también permisos de acceso. "alias" es el mote que queremos tener en la sala. En el servidor de openfire se pude configurar si ese mote debe ser obligatoriamente nuestro nombre de usuario o podemos elegir el que queramos.

Si queremos escribir algo en la sala, el código es sencillo

habitacion.sendMessage("texto a enviar");

y si queremos saber qué ocupantes hay en la sala para sacar una lista de los mismos

Map<String, Occupant> ocupantes = habitacion.getPresences();
Set<String> aliases = ocupantes.keySet();
      for (String alias : aliases) {
         // alias es el mote de uno de los ocupantes.
      }

Interfaz de usuario

La interfaz de usuario puede hacerse con java/gwt, No vamos a dar muchos detalles, sólo lo justo para empezar

Nuestro proyecto necesita depender de los dos jar generados de la librería jaxmpp2. Estos jar contienen los fuentes, los .class y los ficheros gwt.xml correspondientes de la librería. Son los dos jar que comentamos al principio de este artículo. Por supuesto, necesitamos también las librerías de GWT.

En nuestro proyecto hacemos además un fichero gwt.xml que puede parecerse a este

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.2.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.2.0/distro-source/core/src/gwt-module.dtd">
<module>
	<source path="client" />
	<inherits name="com.google.gwt.user.User"/>
	<inherits name="tigase.jaxmpp.gwt.JaxmppGWT" />
	<entry-point class="com.chuidiang.ejemplos.jxmpp2.Chat" />
</module>

donde com.chuidiang.ejemplos.jxmpp2.Chat será la clase java que contenga nuestra ventana de Chat. Esa clase puede tener algo así

public class Chat implements EntryPoint {
   private FlowPanel panelCharla;
   private ScrollPanel scrollCharla;
   private TextArea textAreaTextoEnviar;
   private DockLayoutPanel panelPrincipal;
   private DockLayoutPanel panelEnviar;
   private CellList<String> listaUsuarios;
   private LinkedList<String> usuarios;
   private Button botonEnviar;
   private Label estado;

   private Jaxmpp conexion;
   private MucModule multiChat;
   private Room habitacion;

   /** Un panel principal DockLayoutPanel con tres huecos:
    * En el centro la charla panelCharla con su scrollCharla.
    * A la derecha, la lista de usuario conectados listaUsuarios
    * Abajo un panel panelEnviar con la caja de texto textAreaTextoEnviar y el
    *    boton de enviar botonEnviar
    * Arriba del todo, un label para indicar el estado y posibles errores de
    * conexion.
    */
   @Override
   public void onModuleLoad() {
      panelPrincipal = new DockLayoutPanel(Unit.EM);
      panelEnviar = new DockLayoutPanel(Unit.EM);

      panelCharla = new FlowPanel();
      scrollCharla = new ScrollPanel(panelCharla);

      listaUsuarios = new CellList<String>(new TextCell());
      usuarios = new LinkedList<String>();
      listaUsuarios.setRowData(usuarios);
      panelPrincipal.addEast(listaUsuarios, 10);

      botonEnviar = new Button("enviar");
      panelEnviar.addEast(botonEnviar, 4);
      
      textAreaTextoEnviar  = new TextArea();
      panelEnviar.add(textAreaTextoEnviar);

      panelPrincipal.addSouth(panelEnviar, 3);

      estado = new Label();
      panelPrincipal.addNorth(estado, 2);

      panelPrincipal.add(scrollCharla);

      RootLayoutPanel.get().add(panelPrincipal);

      // Se envia el texto al hacer click en el boton.
      botonEnviar.addClickHandler(new ClickHandler() {
         @Override
         public void onClick(ClickEvent sender) {
            enviaTextoALaHabitacion();
         }
      });

      // Se envia el texto al pulstar intro en la caja de texto
      textAreaTextoEnviar.addKeyUpHandler(new KeyUpHandler() {
         @Override
         public void onKeyUp(KeyUpEvent event) {
            if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
               enviaTextoALaHabitacion();
            }
         }
      });

      // Establecer conexion, obtener sala, etc, como se hizo más arriba
      // en este artículo.
      conectate();

      // Para cierre de conexión cuando la ventana del navegador deje de estar
      suscribeteACierreVentana();
   }

   private void suscribeteACierreVentana() {
      Window.addWindowClosingHandler(new ClosingHandler() {
         @Override
         public void onWindowClosing(ClosingEvent event) {
            cierraConexiones();
         }
      });
      Window.addCloseHandler(new CloseHandler<Window>() {

         @Override
         public void onClose(CloseEvent<Window> event) {
            cierraConexiones();
         }
      });
      attachOnUnloadListener();
   }