J2ME, Java Wireless Message API (WMA)

2
35277

J2ME, Java Wireless Message API (WMA)

Sin lugar a dudas, la mensajería instantánea a
través de mensajes cortos (SMS) es una de las formas de
comunicación más extendida y aceptadas por la sociedad.

En este tutorial voy a intentar hacer una introducción de las
características más importantes que nos proporciona Java
para el envío y recepción de SMS desde aplicaciones para móviles (MIDLets).
Se presupone que el lector ya posee conocimientos básicos de
programación (J2ME, MIDP, CLDC), compilación e
instalación de MIDLets.

Índice de contenidos

Introducción a WMA

WMA, son las siglas de Wireless Message API (curiosamente también lo son de uno de los formatos de audio de Windows, Windows Media Audio),
una extensión de la especificaciones del CLDC y MIDP para el
envio, la recepción y la gestión de SMS desde MIDLets.

A pesar de ser una extensión opcional,
la gran mayoría de los terminales la llevan instalada y lista
para ser usuada desde nuestras aplicaciones J2ME.

WMA es una especificación no una implementación. Su implementación dependerá del terminal y del protocolo de comunicación que use (GSM, CDMA, etc). Por supuesto, nosotros como desarrolladores podemos abstraernos de esto último.

Versiones de la especificación WMA:

Versión 1.0:
Especificación inicial. Describe las funcionales básicas
para el envío y la recepción de SMS. (JSR 120)

Versión 1.1: Una ampliación de la especificación 1.0, para soportar el nuevo modelo de seguridad del MIDP 2.0.

Versión 2.0: Una ampliación a las anteriores para la gestión de mensajes multimedia (MMS). (JSR 205)

Introducción al API WMA

El API está compuesto exclusivamente de interfaces ubicadas bajo el páquete javax.wireless.messaging. Estas interfaces son:

javax.wireless.messaging.Message: Define la funcionalidad genérica de todos los tipos de mensajes. Permite:

  1. Especificar el destinatario del mensaje. public void setAddress(String addr)
  2. Obtener el emisor del mensaje. public String getAddress()
  3. Obtener la fecha de envio del mensaje. java.util.Date getTimestamp()

javax.wireless.messaging.TextMessage: Representa a un mensaje de texto.
Hereda la funcionalidad de javax.wireless.messaging.Message añadiendo los métodos public void setPayloadText(String data) y public String getPayloadText()
para especificar u obtener los datos del mensaje.

javax.wireless.messaging.BinaryMessage: Representa a un mensaje binario.
Hereda la funcionalidad de javax.wireless.messaging.Message añadiendo los métodos public void setPayloadData(byte[] data) y public byte[] getPayloadData()
para especificar u obtener los datos del mensaje.

javax.wireless.messaging.MessageListener: Oyente de mensajes entrantes.
Esta interfaz es útil en javax.wireless.messaging.MessageConnection que funcionan en modo servidor. (Será explicada más adelante.)

javax.wireless.messaging.MessageConnection:
Interfaz a través de la cual se realiza el envío y la
recepción de mensajes. (Será explicada más
adelante.)

¿Qué pasos tengo que realizar para enviar un mensaje (SMS)?

  1. Obtener un MessageConnection en modo cliente (Se verá más adelante).
  2. Crear el mensaje a través de la interfaz MessageConnection.
  3. Especificar el contenido y el destinatario del mensaje.
  4. Usar el método send de la interfaz MessageConnection para enviar el mensaje.

¿Qué pasos tengo que realizar para recibir un mensaje (SMS) de forma asíncrona?

  1. Obtener un MessageConnection en modo servidor (Se verá más adelante).
  2. Implementar la interfaz MessageListener en una de nuestras clases.
  3. Asociar la clase anterior al MessageConnection.
  4. Cuando el método notifyIncomingMessage() de la interfaz MessageListener sea invocado, significa que hemos recibido un mensaje.
  5. Deberemos invocar el método receive() de la interfaz MessageConnection en un hilo independiente.
  6. Realizar el tratamiento del mensaje.

¿Qué pasos tengo que realizar para recibir un mensaje (SMS) de forma síncrona?

  1. Obtener un MessageConnection en modo servidor (Se verá más adelante).
  2. Invocar el método receive() de la interfaz MessageConnection en un hilo independiente (Es un método bloqueante).
  3. Realizar el tratamiento del mensaje.

En J2ME, todas las comunicaciones que requieren los MIDLets con el
exterior (Bluetooth, Socket, Http, etc.) se obtienen a través la
clase javax.microedition.io.Connector que forma parte del CLDC.
Esta clase devuelve una instancia de una clase que implementa la interfaz javax.microedition.io.Connection para el modo de comunicación deseada.
Pues bien, la interface javax.wireless.messaging.MessageConnection no es más que un javax.microedition.io.Connection para comunicación via SMS.

Básicamente a través de está inteface podemos crear, enviar y recibir SMS tanto de forma
síncrona como asíncrona.

¿Cómo obtengo javax.wireless.messaging.MessageConnection?

Los javax.wireless.messaging.MessageConection pueden funcionar de dos modos:

  1. En modo cliente: Sólo sirve para enviar SMS a un destinatario.
  2. En modo servidor: Sirve para recibir y tratar los SMS que son dirigidos hacia el. En esté modo también se pueden enviar SMS.

La forma de especificar el modo de funcionamiento
deseado se realiza a través de la cadena de conexión que
se le pasa a la clase javax.microedition.io.Connector.

Ejemplos de como especificar el modo cliente:

MessageConnection conn = (MessageConnection) javax.microedition.io.Connector.open("sms://+34666666666");

MessageConnection conn = (MessageConnection) javax.microedition.io.Connector.open("sms://+34666666666:5555");

En ambos casos el SMS sería enviado al
teléfono 666666666 de España (por el prefijo +34), pero
con una importante diferencia. En el primer caso, el SMS sería
tratado por la aplicación que por defecto tiene instalada el
teléfono, mientras que, en el segundo caso, el SMS sería
tratado por la aplicación que esté escuchando en ese
puerto.

Ejemplo de como especificar el modo servidor:

MessageConnection conn = (MessageConnection) javax.microedition.io.Connector.open("sms://:5555");

Los SMS que llegen al puerto 5555 serán atendidos por el MIDLet.

En realidad, «5555» NO es un puerto sino un
IDENTIFICADOR que le indica a la plataforma java que desea tratar los
SMS que llegen al terminal con ese identificador.

Métodos de la clase:

public javax.wireless.messaging.Message newMessage(java.lang.String type, java.lang.String address)

Crea un nuevo mensaje para ser enviado.

En el argumento type especificamos el tipo de mensaje que deseamos enviar:

   MessageConnection.TEXT_MESSAGE para mensajes de texto

   MessageConnection.BINARY_MESSAGE para mensajes binarios.

En el argumento address debemos especificar la dirección del destinatario del mensaje.
Normalmente, su número de teléfono.

public int numberOfSegments(javax.wireless.messaging.Message msg)

Devuelve el número de segmentos que son necesarios para enviar la información a través de la red.

Por ejemplo, si queremos enviar el Quijote
via SMS pues seguro que son necesarios más de un segmento (un
segmento es igual a un SMS como lo conocen los usuarios) y este
método nos devolvería o bien un número grande o
bien el valor 0 indicando que no se puede enviar la información
deseada.

public javax.wireless.messaging.Message receive() throws IOException, InterruptedIOException

Devuelve un mensaje enviado a nuestra aplicación. Hay que tener varias cosas importantes en mente:

  1. Es un método bloqueante, por lo que
    generalmente deberá ser invocado en un hilo distinto al hilo
    principal donde está ejecutandose el Midlet.
  2. Nuestra aplicación es responsable de guardar la
    información recibida a memoria no volátil en caso de ser
    necesario.

public void send(javax.wireless.messaging.Message msg) throws IOException, InterruptedIOException

Envia el mensaje al destinatario.

Este método debe ser invocado en un hilo distinto al hilo principal donde está ejecutandose el Midlet.

Para especificar el destinatario del mensaje se debe usar el método setAddress(java.lang.String addr) definido en la interfaz javax.wireless.messaging.Message
de la que heradan javax.wireless.messaging.TextMessage y javax.wireless.messaging.BinaryMessage.

public void setMessageListener(javax.wireless.messaging.MessageListener l) throws IOException

Registrar una clase que implemante la interfaz javax.wireless.messaging.MessageListener, el plataforma J2ME invocará
el método notifyIncomingMessage() cuando reciba un mensaje.

Este método sólo tiene sentido para javax.wireless.messaging.MessageConnection que funcionen en modo servidor

Ejemplo 1. Envío de un SMS en modo texto.

En este ejemplo, vamos a hacer una
aplicación que envie el texto que introduzca el usuario en un
área de texto a un destinatario fijo.

Para el desarrollo de aplicaciones para móviles, yo personalmente utilizo el IDE NetBeans con la extensión Mobility

package autentia.tutoriales.wma;
import javax.microedition.lcdui.*;
improt javax.microedition.midlet.*;

/**
 * MIDLet de Ejemplo del uso del API WMA (Wireless Messagin API) 
 * @author Carlos García. Autentia.
 */ 
public class WMAMidlet extends javax.microedition.midlet.MIDlet {
	private boolean started;
	
	/**
	 * Constructor
	 */
	public WMAMidlet(){
		this.started = false;
	}

	/* 
	 * @see javax.microedition.midlet.MIDlet#startApp()
	 */
	protected void startApp() throws MIDletStateChangeException {
		 
		// Este método puede ser incovado varias veces mientras la aplicación se esté ejecutando. 
		// Por ejemplo, si se está ejecutando la aplicación y nos llega una llamada 
		// entrante, la aplicación generalmente es pausada y esté método será 
		// invocado cuando la llamada finalize.
		
		if (! this.started){	
			this.started = true;
			Display.getDisplay(this).setCurrent(new WMAMainForm(this));
		}
	}
	
	/* 
	 * @see javax.microedition.midlet.MIDlet#destroyApp(boolean)
	 */
	protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
		this.destroyApp(true);
	}

	/* 
	 * @see javax.microedition.midlet.MIDlet#pauseApp()
	 */
	protected void pauseApp() {
		// En este ejemplo no se requiere ninguna tarea cuando el MIDLet es pausado.
	}	

}
package autentia.tutoriales.wma;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.wireless.messaging.*;



/**
 * Ventana principal de la aplicación
 * @author Carlos García. Autentia
 */ 
public class WMAMainForm extends javax.microedition.lcdui.TextBox 
			implements javax.microedition.lcdui.CommandListener {
	
	/**
	 * Referencia al MIDLet
	 */
	private javax.microedition.midlet.MIDlet midlet;
	
	/**
	 * Envia el SMS
	 */
	private javax.microedition.lcdui.Command cmdSend;
	
	/**
	 * Finaliza la aplicación
	 */
	private javax.microedition.lcdui.Command cmdExit;
	
	/**
	 * Constructor
	 * Ventana principal de la aplicación
	 */
	public WMAMainForm(javax.microedition.midlet.MIDlet midlet) {
		super("Mensaje a enviar", "", 166, TextField.ANY);
		this.midlet = midlet;
		
		this.createUI();
	}

	/**
	 * Crea y configura el interfaz gráfico de la ventana.
	 */
	private void createUI(){
		this.setTicker(new Ticker("Autentia Real Business Solutions"));
		this.cmdSend = new Command("Enviar", Command.OK,   1);
		this.cmdExit   = new Command("Salir",  Command.STOP, 1);
		
		this.addCommand(cmdSend);		
		this.addCommand(cmdExit);
		this.setCommandListener(this);		
	}
	
	
	/** 
	  * El usuario desea enviar el SMS 
	 */ 
	private void sendSMSClick() throws java.io.IOException { 
		MessageConnection conn = null; 
		TextMessage msg = null; 
		try { 
			// Paso 1: Obtenemos una implementación del Connection que se encargará de enviar el SMS 
			conn = (MessageConnection) Connector.open("sms://+34699221570"); 
			
			// Paso 2: Creamos el SMS 
			msg = (TextMessage) conn.newMessage(MessageConnection.TEXT_MESSAGE);

			// Paso 3: Establecemos el contenido del SMS 
			msg.setPayloadText(this.getString()); 
			
			// Paso 4: Enviamos el SMS 
			conn.send(msg); 
		} finally { 
			// Paso 5: IMPORTANTE Cerramos la conexión 
			this.closeQuietly(conn); 
			conn = null; 
		} 
	}

      /**
       * Cierra un Connection ignorando todas las posibles excepciones
       */
        private void closeQuietly(javax.microedition.io.Connection conn){
			try {
			    conn.close();
			} catch (Exception ex){
			    // Nada
			}
        }  
      	
	/* 
	 * Receptor de eventos del UI (User Interface)
	 * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable)
	 */
	public void commandAction(Command arg0, Displayable arg1) {
		try {
			if (arg0 == cmdSend){
				this.sendSMSClick();
			} else if (arg0 == cmdExit){
				this.midlet.notifyDestroyed();
			}
		} catch (Exception ex){
			// En caso de error modificamos el texto de la ventana con el mensaje
			this.setString(ex.toString());
		}
	}
}

Ejemplo 2. Envío de un SMS en modo binario.

Los pasos para enviar información en modo binario son los mismos que para el envio de SMS en modo texto.

Los mensajes binarios son representados bajo la clase javax.wireless.messaging.BinaryMessage.
La principal diferencia entre esta clase y javax.wireless.messaging.TextMessage es el método para especificar
el contenido del mensaje a enviar. En este último caso, el contenido es un array de bytes y es especificado
a través del método setPayloadData.

	/**
	* El usuario desea enviar el SMS en modo binario
	*/
	
	private void sendBinarySMSClick() throws java.io.IOException {
		MessageConnection conn = null;
		BinaryMessage msg = null;
		ByteArrayOutputStream bout = null;
		
		try {
			// Paso 1: Obtenemos una implementación del Connection que se encargará de enviar el SMS 
			conn = (javax.wireless.messaging.MessageConnection) Connector.open("sms://+34699221570"); 
			
			// Paso 2: Creamos el SMS 
			msg = (BinaryMessage) conn.newMessage(MessageConnection.BINARY_MESSAGE);
			
			// Paso 3: Establecemos el contenido del SMS con algunos datos de prueba. Por ejemplo, datos de una persona. 
			bout = new ByteArrayOutputStream(); 
			dout = new DataOutputStream(bout); 
			dout.writeBoolean(true);  // ¿Soltero? 
			dout.writeByte(55); // Edad 
			dout.writeUTF("Madrid"); // Provincia de nacimiento 
			dout.writeUTF("España"); // País. 
			dout.writeLong(888883311L); // DNI 
			msg.setPayloadData(bout.toByteArray()); 
			
			// Paso 4: Enviamos el SMS 
			conn.send(msg); 
		} finally { 
			// Paso 5: IMPORTANTE. Cerramos las objetos, liberando recursos 
			this.closeQuietly(bout); 
			this.closeQuietly(dout); 
			this.closeQuietly(conn); 
			dout = null; 
			dout = null; 
			conn = null; 
		} 
	}

    /**
     * Cierra un OutputStream ignorando todas las posibles excepciones
     */        
	private void closeQuietly(java.io.OutputStream out){
        try {
            out.close();
        } catch (Exception ex){
            // Nada
        }
	}
        
    /**
     * Cierra un Connection ignorando todas las posibles excepciones
     */
	private void closeQuietly(javax.microedition.io.Connection conn){
        try {
            conn.close();
        } catch (Exception ex){
            // Nada
        }
	}  	

Introducción a Push Registry.

A partir de la especificación MIDP 2.0, se
añadió una potente característica a la plataforma
J2ME que consiste en que nuestras aplicaciones puedan ser iniciadas por eventos externos o temporizadores.
Por ejemplo, nuestra aplicación puede ser iniciada cuando reciba un SMS en un determinado puerto (identificador).

Haga click aquí, para ver un tutorial de esta característica

Información interesante

A continuación os presento una tabla con información relacionada con
el juego número de SMS necesarios para enviar información.


Referencia:  http://java.sun.com/products/wma/index.jsp

Por lo general los mensajes de texto se envian con
el juego de caracteres del GSM-7 bit, y sólo cuando el mensaje
tiene caracteres que no pueden ser codificados con ese juego de
caracteres se usará el UCS-2.

Conclusiones y reflexiones

En comparación con otros, este API es bastante simple y fácil de utilizar.

Sabiendo utilizar esta y otras tecnologías como las que os presentamos en Autentia a través de nuestros tutoriales, se pueden hacer sistemas interesantes y útiles.

No olvideis que esto es sólo una introducción, asi que
si necesitais más información debeis dirigiros a las
páginas oficiales de la especificación.

Espero que os haya parecido interesante este tutorial.

Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.

cgpcosmad@gmail.com

2 COMENTARIOS

  1. El tutorial esta buenísimo.
    He probado enviar un sms a una móvil pero este no lo puedo leer en el móvil que lo enviando.

    En el móvil de destino no hay aplicaciones servidores en J2ME pero el servicio de sms de telefónica
    me registra el uso del servicio

  2. Hola me parece un excelente tutorial, me ha ayudado mucho en la comprensión del código para el envio de mensajes, me gustaría mucho que pudieses explicar la recepción de mensajes sería interesante para que el tutorial quedara completo.

    De antemano felicidades por el tutorial.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad