PROYECTO FIN DE CARRERAbibing.us.es/proyectos/abreproy/10937/descargar_fichero... · presente...

172
PROYECTO FIN DE CARRERA Implementación de los protocolos JXTA sobre la plataforma J2ME (JXME) UNIVERSIDAD DE SEVILLA ESCUELA SUPERIOR DE INGENIEROS INGENIERÍA DE TELECOMUNICACIÓN DEPARTAMENTO DE INGENIERÍA DE SISTEMAS Y AUTOMÁTICA ÁREA DE INGENIERÍA TELEMÁTICA Alumno: José Pablo Soriano Canela Tutor: Antonio J. Sierra Collado

Transcript of PROYECTO FIN DE CARRERAbibing.us.es/proyectos/abreproy/10937/descargar_fichero... · presente...

PROYECTO FIN DE CARRERA

Implementación de los protocolos JXTA sobre la plataforma J2ME (JXME)

UNIVERSIDAD DE SEVILLA ESCUELA SUPERIOR DE INGENIEROS INGENIERÍA DE TELECOMUNICACIÓN

DEPARTAMENTO DE INGENIERÍA DE SISTEMAS Y AUTOMÁTICA

ÁREA DE INGENIERÍA TELEMÁTICA

Alumno: José Pablo Soriano Canela Tutor: Antonio J. Sierra Collado

AGRADECIMIENTOS

En primer lugar, agradecer a mi familia que me ha ofrecido el apoyo necesario y ha tenido la suficiente paciencia a lo largo de los años

Índice

JXTA sobre J2ME - 1 -

ÍNDICE 0. OBJETIVOS…………………………………………………………………………………...4 1. INTRODUCCIÓN……………………………………………………………………………..5

2. JXTA…………………………………………………………………………………………..8

2.1. INTRODUCCIÓN…………………………………………………………………...8

2.2. ARQUITECTURA JXTA…………………………………………………………...9

2.2.1. COMPONENTES DE JXTA……………………………………………..10

2.3. ARQUITECTURA DE RED……………………………………………………….17

2.3.1. ORGANIZACIÓN DE LA RED…………………………………………17

2.3.2. ÍNDICE DISTRIBUIDO DE RECURSOS COMPARTIDOS…………...18

2.3.3. FIREWALLS Y NAT……….....................................................................19

2.4. PROTOCOLOS JXTA……………………………………………………………..19

2.4.1. PEER DISCOVERY PROTOCOL (PDP)…………....................................20

2.4.2. PEER INFORMATION PROTOCOL (PIP)................................................21

2.4.3. PEER RESOLVER PROTOCOL (PRP)......................................................21

2.4.4. PIPE BINDING PROTOCOL (PBP)..........................................................22 2.4.5. ENDPOINT ROUTING PROTOCOL (ERP)……………………………..22 2.4.6. RENDEZVOUS PROTOCOL (RVP)……………………………………..23

3. J2ME………………………………………………………………………………………….24

3.1. INTRODUCCIÓN………………………………………………………………….24

3.2. CARACTERÍSTICAS……………………………………………………………...24

3.3. COMPONENTES…………………………………………………………………..26

3.3.1. MÁQUINAS VIRTUALES J2ME……………………………………….27

Índice

JXTA sobre J2ME - 2 -

3.3.2. CONFIGURACIONES…………………………………………………...29

3.3.3. PERFILES………………………………………………………………...31

3.4. MIDLETS…………………………………………………………………………..34

3.4.1. GESTOR DE APLICACIONES (AMS)…………………………………35

3.4.2. EL PAQUETE JAVAX.MICROEDITION.MIDLET……………………38

3.4.3. ESTRUCTURA DE UN MIDLET……………………………………….39

3.4.4 DESCARGAS VÍA OTA (OVER THE AIR)……………………………...40

3.5. INTERFACES GRÁFICAS DE USUARIO……………………………………….43

3.5.1. INTERFAZ GRÁFICA DE USUARIO DE ALTO NIVEL……………...45

3.5.2. INTERFAZ GRÁFICA DE USUARIO DE BAJO NIVEL……………...47

3.6. RECORD MANAGEMENT SYSTEM……………………………………………50

3.6.1. RECORD STORES………………………………………………………50

3.6.2. REGISTROS……………………………………………………………...51

3.7. CONECTIVIDAD DE UN DISPOSITIVO MID………………………………….51

3.7.1. CLASES Y CONEXIONES DEL GENERIC CONNECTION FRAMEWORK……………………………………………………………52

3.7.2. COMUNICACIONES HTTP…………………………………………….53 3.7.3. OTRAS CONEXIONES………………………………………………….54

4. JXME (JXTA PARA J2ME)…………………….…………………………………………...56

4.1. INTRODUCCIÓN………………………………………………………………….56

4.2. VERSIÓN CON PROXY………………...………………………………………...56

4.3. VERSIÓN SIN PROXY……………………………………………………………58

5. APLICACIÓN DESARROLLADA……………..…………………………………………...60

5.1. ESTRUCTURA DE LA APLICACIÓN…………………………………………...60

Índice

JXTA sobre J2ME - 3 -

5.1.1. CLASE MICHAT………………………………………………………...61 5.1.2. CLASE OPERACIONES………………………………………………...68 5.1.3. CLASE BUDDY………………………………………………………….76 5.1.4. CLASE CONF……………………………………………………………81 5.1.5. CLASE POLLTHREAD………………………………………………….86 5.1.6. CLASE STATUSUPDATE………………………………………………88

5.2. DESCRIPCIÓN DE LA APLICACIÓN…………………………………………...89

6. INSTALACIÓN Y PRUEBAS DE LA APLICACIÓN……………………………………..93 6.1. INTRODUCCIÓN………………………………………………………………….93

6.2. INSTALACIÓN DE LA APLICACIÓN…………………………………………...93

6.2.1. INSTALACIÓN EN LÍNEA DE COMANDOS……...………………….94

6.2.2. INSTALACIÓN USANDO CVS Y ANT………..………………………95 6.2.3. DESCARGA VÍA OTA…………………………………………………..99

6.3. PRUEBAS DE LA APLICACIÓN……………………………………………….101

7. PLANIFICACIÓN TEMPORAL…………………………………………………………...102 8. CONCLUSIONES………………………………………………………………………..…103 9. BIBLIOGRAFÍA……………………………………………………………………………104 10. CÓDIGO DE LA APLICACIÓN…………………………………………….....................105

Objetivos

JXTA sobre J2ME - 4 -

0. OBJETIVOS

El objetivo del presente proyecto consiste en la implementación de los protocolos JXTA sobre la plataforma J2ME, lo que se conoce como proyecto JXME, y que implica la unión de dos tecnologías de bastante actualidad con un amplio abanico de posibles aplicaciones.

El mundo de las redes P2P, en contraposición al modelo cliente/servidor, supone el acceso

directo a las redes de comunicación de cualquier dispositivo con conectividad, eliminando la figura del servidor que hacía de intermediario en el anterior modelo. De esta forma, podemos salvar algunos problemas típicos de dicho modelo, tales como la saturación en el acceso a los servidores debido al incremento de usuarios de Internet. El proyecto JXTA es un ejemplo de red P2P que tiene como principal característica su independencia frente al lenguaje y plataforma en que se implementen los protocolos que lo conforman.

Por otra parte, la telefonía móvil puede ser uno de los campos en que se ha producido un

mayor crecimiento en número de usuarios en los últimos años, implicando una demanda de servicios y prestaciones de grandes dimensiones. Para proporcionar estas prestaciones, se está imponiendo el uso como plataforma de desarrollo del entorno J2ME, la edición de Java dedicada a dispositivos con restricciones de capacidad, memoria, etc.

Partiendo de estos dos puntos, surge la posibilidad de llevar las tecnologías P2P a los

terminales móviles, en particular los protocolos JXTA y utilizando la J2ME, permitiendo al móvil comunicarse con otros nodos de la red JXTA, compartiendo datos y recursos.

Para lograr el objetivo comentado, con el presente proyecto se ha desarrollado una

aplicación que implementa los protocolos JXTA sobre la plataforma J2ME. Dicha aplicación consiste en un programa de mensajería instantánea en que se utiliza una versión del proyecto JXME (JXTA para J2ME) donde se requiere la presencia de un relay, que sirve al móvil de intermediario con la red JXTA, debido a las limitaciones características de los dispositivos móviles.

Para comprobar el buen funcionamiento de la aplicación mencionada se ha utilizado el

emulador de dispositivos móviles proporcionado por Sun Microsystems, habiéndose realizado una serie de pruebas que se han saldado con resultados satisfactorios.

Introducción

JXTA sobre J2ME - 5 -

CAPÍTULO 1º: INTRODUCCIÓN

En los últimos años, se ha producido un incremento espectacular en el número de usuarios de dispositivos de pequeña capacidad dotados de conectividad, ya sean PDAs, teléfonos móviles, etc. Este aumento ha conllevado la aparición de un gran mercado que ha demandado una amplia gama de servicios y prestaciones.

Entre las prestaciones que se han desarrollado para dichos dispositivos se encuentran las

tecnologías punto-a-punto (P2P), siendo uno de sus exponentes el proyecto JXTA. En dichas tecnologías se elimina la utilización de nodos centralizados o servidores a los que conectarse para realizar cualquier actividad, como intercambio de archivos o participación en tareas comunes, permitiendo la conexión directa entre los distintos nodos participantes.

Para aplicar el proyecto JXTA a los dispositivos móviles se ha utilizado un subconjunto del

entorno Java conocido como J2ME, en el que se tienen en cuenta las limitaciones propias de tales terminales.

De la aplicación de los protocolos JXTA sobre la plataforma J2ME es de lo que trata el

presente proyecto, incluyendo una aplicación en la que se va a desarrollar un programa de mensajería instantánea o chat.

La tecnología JXTA es un conjunto de protocolos abiertos P2P que permite que cualquier

dispositivo conectado a una red pueda actuar como nodo comunicándose con el resto de nodos que componen la red. Estos protocolos son independientes del lenguaje de programación, existiendo implementaciones para múltiples entornos. Los nodos JXTA crean una red virtual donde cualquier nodo puede interactuar con otros nodos y recursos directamente, incluso cuando se encuentren tras firewalls o NATs.

El proyecto JXTA comenzó como un proyecto de investigación incubado en Sun

Microsystem bajo la tutela de Bill Joy y Mike Clary, para proporcionar un espacio peer-to-peer

a cualquier dispositivo capaz de conectarse a una red de comunicación. Todo surgió a comienzos de 2001 como tema en una conferencia, siendo creada la página

principal del proyecto www.jxta.org en abril de dicho año. Desde el principio, la filosofía del proyecto ha sido la participación de todo aquél interesado en colaborar, pudiéndose unir a los distintos subproyectos que han ido surgiendo con aplicaciones basadas en los protocolos JXTA.

En la actualidad, se pueden encontrar una gran variedad de proyectos, que abarcan desde la

investigación en las propias APIs de JXTA, servicios P2P o aplicaciones que implementan los distintos protocolos.

En resumen, el mundo JXTA es aún algo bastante nuevo, en constante fase de estudio y de

cambio, no estando sus aplicaciones optimizadas debido a esta permanente actualización.

Introducción

JXTA sobre J2ME - 6 -

Por otra parte, la edición Java 2 Micro Edition fue presentada en 1999 por Sun Microsystems con el propósito de habilitar aplicaciones Java para pequeños dispositivos. Podemos decir que Java Micro Edition es la versión del lenguaje Java que está orientada al desarrollo de aplicaciones para dispositivos pequeños con capacidades restringidas tanto en pantalla gráfica, como de procesamiento y memoria (teléfonos móviles, PDA`s, Handhelds, Pagers, etc).

Esta tecnología ha aparecido relativamente tarde (a finales de los 90 frente a la tecnología Java que nació a mediados), pudiéndose deberse esto a que las necesidades de los usuarios de telefonía móvil han cambiado mucho en estos últimos años y cada vez demandan más servicios y prestaciones por parte tanto de los terminales como de las compañías.

Además, el uso de esta tecnología depende del asentamiento en el mercado de otras, como

GPRS, íntimamente asociada a J2ME y que no ha estado a nuestro alcance hasta hace poco. J2ME es la tecnología del futuro para la industria de los dispositivos móviles, estando implantada en la actualidad en la práctica totalidad de móviles que van apareciendo en el mercado.

A continuación vamos a pasar a describir la distribución que se ha seguido a la hora de

redactar la presente documentación. En primer lugar, tras el índice, se realizará una breve descripción de los objetivos que se han

pretendido alcanzar con la ejecución del presente proyecto y a continuación, se hará una pequeña introducción a los temas que vamos a tocar después, adjuntándose la descripción de la estructura de la memoria que nos ocupa actualmente.

En el capítulo segundo, pasaremos a describir el proyecto JXTA, desarrollando su

estructura, protocolos, etc. El capítulo tercero está dedicado a la plataforma J2ME , enumerando sus componentes,

clases, interfaz gráfica, etc. En el capítulo cuarto se definirá qué es JXME ( JXTA para J2ME), así como las dos

versiones disponibles(con y sin proxy). En el capítulo quinto, se explicará el funcionamiento de la aplicación desarrollada para el

presente proyecto, consistente en un programa de mensajería instantánea que implementa los protocolos JXTA en un emulador de teléfono móvil.

En el capítulo sexto se enumerarán los pasos seguidos para su instalación y las pruebas

realizadas. En el capítulo septimo, describiremos la planificación temporal con la que se ha ejecutado el

proyecto. En el octavo capítulo, se especificarán las conclusiones obtenidas con la realización del

presente proyecto así como se referirán las posibles líneas futuras de trabajo.

Introducción

JXTA sobre J2ME - 7 -

En el capítulo noveno incluimos una bibliografía con los documentos consultados tanto para

la ejecución del proyecto como para la redacción de la memoria. Por último, y a modo de anexo, se incluye el código Java de la aplicación desarrollada,

dividido en las clases que lo componen.

JXTA

JXTA sobre J2ME - 8 -

CAPÍTULO 2º: JXTA 2.1. INTRODUCCIÓN

JXTA es un conjunto de protocolos abiertos peer-to-peer (P2P: punto a punto) que permite que cualquier dispositivo conectado a una red (ya sea un PC, una PDA, un móvil, etc.), pueda actuar como nodo comunicándose con el resto de nodos que conforman la red. Estos protocolos son independientes del lenguaje de programación, existiendo implementaciones para múltiples entornos.

El término JXTA procede de la palabra inglesa juxtapose (yuxtaposición), con lo que se

quiere recalcar que se trata de un modelo computacional yuxtapuesto (no está diseñado para sustituir sino para ampliar y satisfacer un nuevo conjunto de necesidades) al actual modelo predominante conocido como cliente-servidor, en el que una serie de equipos centralizados llamados servidores, atienden y resuelven las peticiones realizadas por los distintos clientes autorizados que utilizan un determinado servicio.

Pero, ya sea en el caso en el que el recurso que se solicita no se encuentra en un servidor sino

en un equipo determinado, o el caso en el que no podemos acceder al servidor que contiene la información que requerimos, surge la necesidad de otro modelo en el que los equipos finales sean capaces de comunicarse entre sí, sin la necesidad de realizarlo a través de un nodo central.

Aquí es donde aparecen la computación distribuida y las tecnologías peer-to-peer por las que

se proporciona acceso directo a los recursos de un dispositivo (nodo) a otro, sin utilizar un control central. Ejemplos de servicios P2P distribuidos son los programas para intercambios de archivos tipo Emule, Kazaa, etc.

JXTA se basa en la tecnología punto a punto, proporcionando un mecanismo que permitirá

crear una nueva gama de servicios y aplicaciones a la vez que amplía los servicios Web desde ubicaciones fijas o centralizadas hasta cualquier punto de la red. Ejemplos de dichos servicios y aplicaciones son:

• Realizar búsquedas en toda la Web y en todos los dispositivos conectados (no sólo en los

servidores) para encontrar la información que se necesita. • Compartir servicios de computación, tales como procesadores o sistemas de

almacenamiento, independientemente del lugar en el que estén ubicados físicamente los usuarios.

• Colaborar en proyectos desde cualquier lugar, utilizando un dispositivo conectado. • Participar en subastas entre grupos de usuarios seleccionados. • Conectar sistemas de juegos para que varios usuarios ubicados en distintos lugares

puedan disfrutar del mismo juego de forma interactiva.

JXTA

JXTA sobre J2ME - 9 -

Las características principales que debe cumplir la tecnología JXTA son:

• Interoperabilidad: para permitir la localización y comunicación de los nodos entre sí por medio de servicios P2P.

• Independencia: debe ser independiente de los lenguajes de programación (Java, C/C++, Perl, etc), protocolos de transporte (TCP/IP, HTTP, Bluetooth, HomePNA, etc.) y plataformas de desarrollo donde se ejecute.

• Accesibilidad: está diseñada para ser accesible por cualquier dispositivo, no sólo por PCs, en cualquier plataforma de desarrollo.

Los protocolos que conforman la tecnología JXTA estandarizan la forma en que los nodos:

• Se descubren unos a otros. • Se auto-organizan dentro de grupos de nodos. • Anuncian y descubren servicios. • Se comunican con otros nodos. • Monitorizan al resto.

2.2. ARQUITECTURA JXTA

La arquitectura JXTA está dividida en tres capas como vemos en la siguiente figura:

Figura 2.1: Arquitectura JXTA

• Núcleo JXTA (JXTA Core): pertenecen a esta capa las primitivas esenciales que son

comunes en una red P2P, tales como la creación de nodos y grupos, primitivas de seguridad, mecanismos de búsqueda y transporte (incluido manejo de firewalls), etc.

JXTA

JXTA sobre J2ME - 10 -

• Capa de servicios (JXTA Services): incluye servicios de red que no son estrictamente necesarios en una red P2P, aunque si puedan resultar deseables. Ejemplos de tales servicios son mecanismos de búsqueda e indexado, tratamiento de directorios, sistemas de almacenamiento, compartimiento de ficheros, cesión de recursos, sistemas de ficheros distribuidos, autenticación, servicios PKI (Public Key Infrastructure), etc.

• Capa de aplicaciones (JXTA Applications): incluye implementaciones de aplicaciones, tales como mensajería instantánea P2P, compartimiento de ficheros y recursos, sistemas de correo P2P, sistemas de subasta P2P, etc.

2.2.1. COMPONENTES DE JXTA

La red JXTA está formada por una serie de nodos interconectados (peers) que se pueden

agrupar dentro de grupos (peer groups), que proporcionan una conjunto de servicios comunes (tales como compartimiento de documentos o aplicaciones de chat).

Los nodos JXTA anuncian sus servicios por medio de documentos XML llamados advertisements, permitiendo que el resto de nodos de la red sepan como conectarse e interactuar con dichos servicios. Los nodos usan tuberías (pipes) para enviar mensajes a otros nodos.

Vamos a pasar a describir los componentes de una red JXTA uno a uno. 2.2.1.1. Nodos (Peers)

Un nodo es cualquier dispositivo que implementa uno o más protocolos JXTA. Pueden ser PCs, PDAs, móviles, servidores, etc. Cada nodo actúa asíncrona e independientemente del resto de nodos y es unívocamente identificado por un identificador llamado Peer ID.

Los nodos publican uno o más interfaces de red para usar con los protocolos JXTA. Cada

interfaz publicada es anunciada como un punto final de nodo (peer endpoint), que identifica unívocamente la interfaz de red. Estos puntos finales son usados por los nodos para establecer conexiones directas punto a punto entre dos nodos.

No todas las conexiones entre nodos deben ser directas, sino que puede haber nodos

intermedios en aquellos casos en que los nodos origen y destino estén separados debido a conexiones de redes físicas y a distintas configuraciones de red (NATS, firewalls, proxies, etc.). 2.2.1.2. Grupos de Nodos (Peer Groups)

Un grupo es una colección de nodos que han acordado ofrecer una serie de servicios comunes. Cada grupo cuenta con un único identificador llamado peer group ID y establece su propia política de admisión de miembros, desde admitir a cualquiera hasta requerir una serie de credenciales determinadas.

Los nodos pueden pertenecer a más de un grupo simultáneamente. Por defecto, el primer

grupo que es inicializado es el Net Peer Group. Todos los nodos pertenecen al Net Peer Group y pueden elegir si unirse a grupos adicionales.

JXTA

JXTA sobre J2ME - 11 -

Los protocolos JXTA describen como los nodos pueden anunciar, descubrir, unirse y monitorizar grupos de nodos.

Algunas de las razones para crear grupos se describen a continuación:

• Para crear entornos seguros: los grupos crean un dominio local de control con una

determinada política de seguridad (como un nombre de usuario y una contraseña). Esto permite a los miembros limitar el acceso a los recursos del grupo.

• Para crear entornos de especialización: los grupos establecen un dominio local formado por nodos con los mismos intereses (por ejemplo una red de compartimiento de CPUs).

• Para crear un entorno de monitorización: con ello se puede monitorizar un conjunto de nodos con diversos motivos (por ejemplo para efectos de facturación).

Los grupos forman una estructura jerárquica, en la que cada grupo tiene un grupo padre. El

advertisement del grupo es publicado en el grupo padre. Un grupo de nodos proporciona una serie de servicios (peer group services). Para que dos

nodos puedan interactuar por medio de un servicio, esos dos nodos deben pertenecer al mismo grupo. JXTA define un núcleo de servicios aunque es posible desarrollar servicios adicionales para cada actividad específica. El núcleo de servicios está formado por:

• Discovery Service: es usado por los nodos miembros para buscar recursos de grupo, tales como nodos, grupos de nodos, tuberías y servicios.

• Membership Service: es usado por los miembros para aceptar o rechazar una solicitud de inclusión en el grupo. Los nodos que deseen unirse a un grupo deben buscar primero a un nodo miembro y solicitarle la inclusión. La solicitud es estudiada por el colectivo de nodos miembros que, ya sea por medio de votación o eligiendo a un subgrupo encargado de la admisión de nuevos miembros, determina si es aceptada o rechazada.

• Access Service: es usado para validar las solicitudes de un nodo a otro. El nodo receptor proporciona las credenciales del nodo transmisor e información sobre la solicitud para determinar si es permitida. No todas las acciones dentro de un grupo necesitan ser comprobadas por el servicio, solo aquéllas que están limitadas para algunos nodos.

• Pipe Service: es usado para crear y controlar las tuberías entre los nodos miembros del grupo.

• Resolver Service: es usado para enviar solicitudes genéricas de información a otros nodos (por ejemplo comprobar el estado de un determinado servicio).

• Monitoring Service: es usado para permitir a un nodo monitorizar a otros miembros del mismo grupo.

No todos los grupos deben implementar todos los servicios vistos, sino aquéllos que encuentre útiles para su funcionamiento normal, obteniendo del grupo Net Peer Group implementaciones genéricas del resto de servicios.

JXTA

JXTA sobre J2ME - 12 -

2.2.1.3. Servicios de Red Los nodos cooperan para publicar, descubrir (por medio del Peer Discovery Protocol) e

invocar servicios de red. Se establecen dos niveles de servicios de red:

• Peer Services: los servicios de nodo son sólo accesibles en el nodo que publica dicho

servicio. Si falla ese nodo, el servicio también falla. Se pueden ejecutar múltiples instancias de un servicio en diferentes nodos, pero cada instancia publicará su propio advertisement.

• Peer Group Services: los servicios de grupo están compuestos por una colección de instancias (que pueden colaborar potencialmente entre ellas) de un servicio que está ejecutándose en múltiples miembros del grupo. Si un nodo falla, el servicio no se ve afectado. Los servicios de grupo son publicados como parte del advertisement del grupo.

Los servicios pueden ser preinstalados en un nodo o cargados desde la red. 2.2.1.4. Módulos

Los módulos JXTA son abstracciones usadas para representar cualquier trozo de código que implemente un comportamiento en JXTA. Los servicios de red son el ejemplo más común de comportamiento que se puede dar en un nodo. La abstracción no especifica cuál es el código, ni en el lenguaje ni la plataforma en la que va a ser implementado.

Cuando un nodo se une a un nuevo grupo, puede darse el caso de que encuentre nuevos

comportamientos que debe inicializar para poder formar parte de dicho grupo (por ejemplo un nuevo servicio de búsqueda que sólo es usado en ese grupo).

El uso de módulos permite la representación de comportamientos independientes de la

plataforma que se use y ayuda a los nodos a especificar cualquier tipo de implementación de dichos comportamientos.

La habilidad para describir y publicar comportamientos independientes es esencial a la hora

de soportar grupos compuestos por nodos heterogéneos (uno de los objetivos fundamentales de JXTA).

Las abstracciones de módulo incluyen una clase, una especificación y una implementación:

• Module Class: es usada principalmente para anunciar la existencia de un

comportamiento. La definición de la clase representa un comportamiento y una relación esperados para soportar el módulo. Cada clase es identificada por un único ModuleClass

ID. • Module Specification: es usada principalmente para acceder al módulo. Contiene toda la

información necesaria para acceder e invocar al módulo. En el caso de un servicio, la especificación podría contener el advertisement de una tubería con la que comunicarse

JXTA

JXTA sobre J2ME - 13 -

con el servicio. La especificación proporciona un acercamiento a la funcionalidad que una clase implica. Puede haber múltiples especificaciones para una clase dada. Cada especificación es identificada por un único ModuleSpecID. Éste contiene el ModuleClass

ID de la clase asociada. Una especificación implica compatibilidad de red. Todas las implementaciones de una especificación dada deben usar los mismos protocolos y ser compatibles, aunque estén escritas en lenguajes diferentes.

• Module Implementation: es la implementación de una especificación dada. Puede haber

múltiples implementaciones para cada especificación de módulo, cada una conteniendo el ModuleSpecID de dicha especificación.

Los módulos pueden ser usados tanto por los servicios de grupo como por los servicios individuales. Tanto las clases, como las especificaciones, como las implementaciones, tienen asociados un advertisement que puede ser publicado y descubierto por otros nodos JXTA. 2.2.1.5. Tuberías (Pipes)

Los nodos usan las tuberías para enviarse mensajes entre ellos. Las tuberías son mecanismos

de transferencia de mensajes asíncronos y unidireccionales. Los extremos de una tubería (pipe endpoints) son conocidos como input pipe (el lado

receptor) y output pipe (el lado transmisor). Los extremos de las tuberías son dinámicamente asignados a los puntos finales de los nodos (peer endpoints) en tiempo de ejecución. Los puntos finales de un nodo se corresponden con una interfaz de red disponible (por ejemplo un puerto TCP y una dirección IP asociada) que puede ser usada para enviar y recibir mensajes.

Las tuberías son canales de comunicación virtual y pueden permitir conectar nodos que no

tienen una comunicación directa. En este caso, se requiere la presencia de uno o más nodos intermedios que retransmitan los mensajes.

Las tuberías ofrecen dos modos de comunicación, punto a punto (unicast) y propagación

(propagate) como vemos en la Figura 2.2. El núcleo de JXTA también contiene un tipo especial de tubería llamada secure unicast, que es una variante segura de la tubería punto a punto.

• Point-to-point Pipes: conectan dos extremos de tubería: un input pipe en un nodo que

recibe mensajes enviados desde el output pipe de otro nodo. • Propagate Pipes: conectan un output pipe a varios input pipes. La propagación se da

siempre dentro de un mismo grupo. • Secure Unicast Pipes: es un tipo de tubería punto a punto que proporciona un canal

seguro de comunicación.

Se pueden construir tipos adicionales a partir de las tuberías básicas (por ejemplo tuberías bidireccionales y tuberías bidireccionales/seguras).

JXTA

JXTA sobre J2ME - 14 -

Figura 2.2: Tuberías punto a punto y de propagación

2.2.1.6. Mensajes (Messages)

Los nodos JXTA se intercambian mensajes para comunicarse entre sí. El mensaje es la unidad básica de intercambio de datos. Los mensajes son enviados y recibidos por los servicios Pipe Service y Endpoint Service. Normalmente las aplicaciones usan el servicio de tuberías (Pipe Service) para crear, enviar y recibir mensajes. El Endpoint Service sólo lo usan las aplicaciones que necesitan entender y controlar la topología de la red JXTA.

Un mensaje es una secuencia ordenada de componentes llamados elementos del mensaje

(message elements). Cada mensaje es un conjunto de parejas nombre/valor de un determinado tipo.

Los protocolos JXTA son especificados como un conjunto de mensajes intercambiados entre

los nodos. Cada plataforma de desarrollo describe de una forma distinta como un mensaje es convertido desde y a la estructura de datos original.

Existen dos representaciones para los mensajes: XML y en forma binaria. Los servicios

pueden usar la forma más apropiada para su transporte (si un servicio requiere una representación compacta para un mensaje usaría la representación binaria, por ejemplo JXTA J2SE usa formato binario). Los datos binarios pueden ser codificados en Base64 en el cuerpo de un mensaje XML.

El uso de mensajes XML para definir protocolos permite que muchos tipos distintos de

nodos puedan usar ese protocolo. Cada nodo lo implementa de la mejor forma que sus características se lo permiten. Si un nodo sólo necesita un subconjunto del mensaje, puede identificar dentro de él las partes que le son útiles e ignorar el resto. 2.2.1.7. Advertisements

Todos los recursos de una red JXTA (nodos, grupos, tuberías, y servicios) están representados por un advertisement. Los advertisements son estructuras de datos representadas como documentos XML. Los nodos descubren los distintos recursos buscando su correspondiente advertisement y pueden almacenar localmente los ya descubiertos.

Cada advertisement es publicado con un tiempo de vida que especifica la disponibilidad del

recurso asociado. Este tiempo de vida permite la eliminación de recursos obsoletos sin que sea

JXTA

JXTA sobre J2ME - 15 -

requerida la presencia de un control central. Para extender el tiempo de vida, el advertisement

puede ser re-publicado. Los protocolos JXTA definen los siguientes tipos de advertisement:

• Peer Advertisement: describe los recursos de un nodo. Mantiene información sobre un

nodo, tal como su nombre, su identificador, los puntos finales disponibles y cualquier atributo que un servicio de grupo de ese nodo quiera publicar (por ejemplo si ese nodo es un nodo de encuentro (rendezvous peer) para el grupo).

• Peer Group Advertisement: describe los recursos específicos de un grupo, tales como su nombre, su identificador, una descripción, especificaciones y parámetros de servicio.

• Pipe Advertisement: describe un canal de comunicación y es usado por el servicio de tuberías para crear los extremos asociados. Cada advertisement de una tubería contiene un identificador opcional, el tipo de tubería (punto a punto, propagación, segura, etc) y un identificador de tubería único.

• Module Class Advertisement: describe una clase de un módulo. Su principal objetivo es formalizar la existencia de una clase. Incluye un nombre, una descripción, y un identificador (ModuleClassID).

• Module Spec Advertisement: define una especificación de un módulo. Su objetivo primario es proporcionar la información necesaria para crear implementaciones conforme a la especificación. También facilita el uso remoto de la especificación al proporcionar información tal como el advertisement de la tubería con la que comunicarnos con el servicio. Incluye un nombre, una descripción, un identificador unívoco (ModuleSpecID), un advertisement de tubería y un campo con parámetros para ser interpretados por cada implementación.

• Module Impl Advertisement: define una implementación de una determinada especificación. Incluye un nombre, el ModuleSpecID asociado, así como código, parámetros, etc. que un nodo pueda necesitar para ejecutar la implementación.

• Rendezvous Advertisement: describe un nodo que actúa de nodo de encuentro (rendezvous peer) para un determinado grupo.

• Peer Info Advertisement: publica información sobre el estado actual de un nodo, tal como la cuenta de mensajes enviados y recibidos, el tiempo en que se recibió y en que se envió el último mensaje, etc.

Cada advertisement es representado por un documento XML, en el que se dan una serie de

elementos ordenados jerárquicamente. Cada elemento puede tener atributos formados por una pareja nombre/valor.

En la siguiente figura vemos un ejemplo del advertisement de una tubería, pudiendo apreciar

los distintos campos tales como el tipo de componente al que pertenece el advertisement, el identificador de la tubería, el tipo de tubería de que se trata y el nombre de la tubería.

JXTA

JXTA sobre J2ME - 16 -

<?xml version="1.0"?> <!DOCTYPE jxta:PipeAdvertisement> <jxta:PipeAdvertisement xmlns:jxta="http://jxta.org"> <Id> urn:jxta:uuid59616261646162614E504720503250338E3E786229EA460DADC 1A176B69B731504 </Id> <Type> JxtaUnicast </Type> <Name> TestPipe.end1 </Name> </jxta:PipeAdvertisement>

Tabla 2.1: Ejemplo de advertisement de una tubería

2.2.1.8. Seguridad

Los nodos JXTA operan bajo un modelo basado en la confianza garantizada por otro nodo para realizar una determinada tarea. Se deben proporcionar cinco elementos básicos de seguridad:

• Confidencialidad: garantiza que el contenido de un mensaje no será revelado a individuos no autorizados.

• Autenticación: garantiza que el emisor es quien dice ser. • Autorización: garantiza que el emisor está autorizado para enviar el mensaje. • Integridad en los datos: garantiza que el mensaje no ha sido modificado accidental o

deliberadamente en el camino. • Refutabilidad: garantiza que el mensaje fue transmitido por un emisor apropiadamente

identificado y no es una copia de un mensaje previamente enviado.

Los mensajes XML proporcionan la capacidad de añadir credenciales, certificados y claves a los mensajes JXTA. Los mensajes pueden ser encriptados (con claves públicas) y firmados (con certificados) para dar confidencialidad y refutabilidad. Las credenciales pueden usarse para proporcionar autenticación y autorización.

En las comunicaciones con varios saltos, debe existir confianza entre cada nodo intermedio

ya que la seguridad está comprometida si alguno de los enlaces no es seguro.

2.2.1.9. Identificadores

Cualquier recurso JXTA debe estar unívocamente identificado y proporciona una referencia única a dicha entidad. Existen seis tipos de recursos JXTA que cuentan con un tipo de identificador definido: nodos, grupos, tuberías, contenidos, clases y especificaciones.

Para representar un identificador se usa URN (Uniform Resource Name), un tipo especial de

URI (Uniform Resource Identifier) que proporciona identificadores para recursos persistentes e independientes de la localización.

JXTA

JXTA sobre J2ME - 17 -

Existen dos identificadores reservados en JXTA: NULL ID y Net Peer Group ID. En JXTA J2SE, los identificadores unívocos son generados aleatoriamente. 2.3. ARQUITECTURA DE RED 2.3.1. ORGANIZACIÓN DE LA RED

La red JXTA es una red ad hoc, multisalto y adaptable, compuesta de nodos interconectados, en donde el rutado de mensajes no está determinado previamente. Los nodos pueden unirse o dejar la red en el momento en que deseen y las rutas suelen cambiar frecuentemente.

Los nodos pueden ser cualquier equipo capaz de usar los protocolos JXTA. Se suelen dividir

en cuatro tipos:

• Minimal edge peer: estos nodos pueden enviar y recibir mensajes pero no almacenan los advertisements ni redireccionan mensajes para otros nodos. Suelen ser dispositivos con recursos limitados (PDAs y teléfonos móviles).

• Full-featured edge peer: pueden enviar y recibir mensajes y almacenar advertisements. Pueden contestar a las solicitudes de búsqueda con la información guardada en los advertisements almacenados pero no las transmiten más allá. La mayoría de los nodos son de este tipo.

• Rendezvous Peer: es como cualquier otro nodo, almacenando los advertisements, pero puede transmitir las solicitudes de búsqueda de otros equipos, ayudándolos a descubrir recursos. Cuando un nodo se une a un grupo, automáticamente busca un nodo de encuentro (rendezvous peer). Si no lo encuentra, se convierte dinámicamente en nodo de encuentro de ese grupo. Cada nodo de encuentro mantiene una lista de otros nodos de encuentro conocidos y también de los nodos que lo están utilizando como nodo de encuentro. Cada grupo puede tener tantos nodos de encuentro como necesite. Sólo los nodos de encuentro de un determinado grupo verán las solicitudes de búsqueda específicas de grupo. Los nodos finales envían solicitudes a los nodos de encuentro y las que éstos no pueden contestar, las envían a otros nodos de encuentro conocidos. Este proceso continúa hasta que se da una respuesta a la solicitud o hasta que la solicitud muere. Los mensajes tienen un tiempo de vida por defecto (TTL: time to live) de siete saltos. Los bucles son prevenidos manteniendo la lista de nodos a través de los que ha pasado el mensaje.

• Relay Peer: un nodo de retransmisión almacena información sobre las rutas hacia otros nodos, enviándoles los mensajes hacia ellos dirigidos. Un nodo, primero busca en su caché local si tiene almacenada la ruta. Si no la encuentra, envía una consulta (query) a los nodos de retransmisión pidiendo información para direccionar el mensaje. Los nodos de retransmisión también transmiten mensajes en nombre de nodos que no pueden directamente alcanzar otro nodo ( Ej; entornos NAT), puenteando redes físicas y lógicas.

Cualquier nodo puede actuar como nodo de encuentro y de retransmisión, pudiéndose dar en

un mismo nodo a la vez los dos comportamientos.

JXTA

JXTA sobre J2ME - 18 -

2.3.2. ÍNDICE DISTRIBUIDO DE RECURSOS COMPARTIDOS

La plataforma JXTA 2.0 J2SE proporciona un servicio de índice distribuido de recursos compartidos (SRDI: Shared Resource Distributed Index) que permite un mecanismo más eficiente para propagar las solicitudes de consulta (query) dentro de la red JXTA. Los nodos de reunión mantienen un índice con los advertisements publicados por los nodos finales. Cuando un nodo final publica un nuevo advertisement, usan el SRDI para que su nodo de encuentro lo almacene. Luego, gracias a este servicio, limitamos a que los queries se propaguen sólo entre los nodos de encuentro, reduciendo el número de nodos involucrados en las búsquedas de advertisements.

Cada nodo de encuentro mantiene su propia lista de nodos de encuentro conocidos dentro del

grupo, y periódicamente, selecciona un número aleatorio de ellos, y les envía una lista aleatoria de sus nodos de encuentro conocidos. Periódicamente también, eliminan los nodos de encuentro que no responden.

Cuando un nodo publica un nuevo advertisement, éste es indexado por el SRDI usando como

índice su nombre o su identificador. Sólo los índices son almacenados por el nodo de encuentro, minimizando la cantidad de información a ser almacenada. Los nodos de encuentro también promueven el índice a nodos de encuentro adicionales (seleccionados por el cálculo de una función de comprobación aleatoria del índice del advertisement).

2.3.2.1. Consultas

Cuando un nodo final inicia una petición de búsqueda, se envía inicialmente a su nodo de encuentro y también por difusión a los otros nodos de la misma subred.

En una búsqueda local, los nodos receptores de la consulta responden directamente al par emisor, si contienen la información en su memoria caché local.

Las consultas fuera de la vecindad local se envían al nodo de encuentro al que está conectado. Éste trata de satisfacer la consulta buscando en su memoria caché. Si contiene la información demandada, entonces contesta directamente al nodo que hizo la petición y no sigue propagando la petición. Si contiene el índice para el recurso en su SRDI, entonces contactará con el nodo que publicó el recurso y ese nodo responderá directamente al nodo que hizo la petición (los nodos de encuentro almacenan sólo el índice del advertisement y no el advertisement en sí).

Si el nodo de encuentro no contiene la información demandada, entonces se usa un algoritmo predeterminado que recorre el conjunto de nodos de encuentro conocidos, buscando el que contenga el índice. Se usa una cuenta de saltos para determinar el número máximo de veces que la petición puede ser reenviada. Una vez que la consulta alcanza al nodo destinatario, éste contesta directamente al que originó de la consulta.

JXTA

JXTA sobre J2ME - 19 -

2.3.3. FIREWALLS Y NAT

Un nodo detrás de un firewall puede enviar un mensaje directamente a un nodo fuera de él. Pero un nodo fuera del firewall no puede establecer una conexión directamente con otro que se encuentre detrás del firewall.

Para que los nodos JXTA se comuniquen entre sí a través un firewall, se deben dar las siguientes condiciones:

• Al menos un nodo en el grupo que está dentro del firewall debe conocer a al menos un

nodo de fuera del firewall. • El nodo interior y el nodo exterior del firewall deben conocer al otro y deben soportar

HTTP. • El firewall tiene que permitir transferencias de datos HTTP.

Estas transferencias HTTP a través del firewall no necesitan estar restringidas al puerto 80,

aunque ese es el puerto usado por defecto por la mayoría de navegadores de Internet y por el actual JXTA J2SE.

Cuando dos nodos quieren pasarse un mensaje pero el firewall les impide comunicarse

directamente, el nodo emisor primero hace una conexión a un nodo retransmisor usando un protocolo como HTTP que puede penetrar el firewall. Luego el nodo retransmisor hace una conexión al nodo receptor, usando un protocolo como TCP/IP. Se establece una conexión virtual entre los dos nodos finales.

2.4. PROTOCOLOS JXTA

JXTA define una serie de formatos de mensaje XML, o protocolos, para la comunicación entre nodos. Los nodos usan estos protocolos para descubrirse unos a otros, anunciar y descubrir recursos de la red, y comunicar y encaminar los mensajes.

Hay seis protocolos JXTA:

• Peer Discovery Protocol (PDP): usado por los nodos para anunciar sus recursos (por

ejemplo nodos, grupos de nodos, tuberías o servicios) y descubrir recursos de otros nodos. Cada recurso del nodo se describe y publica usando un advertisement.

• Peer Information Protocol (PIP): usado por los nodos para obtener información acerca del estado (el tiempo activo, el estado, el tráfico reciente, etc.) de otros nodos.

• Peer Resolver Protocol (PRP): permite a los nodos enviar una consulta genérica a uno o más nodos y recibir una respuesta (o respuestas múltiples) para la consulta. Las consultas pueden ser dirigidas a todos los nodos de un grupo de nodos o a nodos específicos dentro del grupo. A diferencia de PDP y PIP, los cuáles se usan para consultar información específica predefinida, este protocolo permite a los servicios de nodo definir e intercambiar cualquier información arbitraria que necesiten.

JXTA

JXTA sobre J2ME - 20 -

• Pipe Binding Protocol (PBP): usado por los nodos para establecer un canal de comunicación virtual, tubería o pipe, entre varios nodos. El PBP es usado por un nodo para unir dos o más finales de la conexión (los extremos de una tubería).

• Endpoint Routing Protocol (ERP): usado por los nodos para encontrar rutas (caminos) a los puertos de destino en otros nodos. La información de la ruta incluye una secuencia ordenada de identificadores de nodos de retransmisión que pueden usarse para enviar un mensaje al destino.

• Rendezvous Protocol (RVP): el mecanismo por el cuál los nodos pueden subscribirse o pueden ser un suscriptor de un servicio de propagación. Dentro de un grupo, los nodos pueden ser nodos de encuentro o nodos que están escuchando a nodos de encuentro. El RVP permite a un nodo enviar mensajes a todas las instancias del servicio que estén escuchando. El RVP es usado por el Peer Resolver Protocol y el Pipe Binding Protocol para propagar mensajes.

Todos los protocolos JXTA son asíncronos, y se basan en un modelo de consulta/respuesta.

Los nodos JXTA usan los protocolos para enviar una consulta a uno o más nodos en su grupo.

Los nodos JXTA no están obligados a implementar los seis protocolos, sólo los que vayan a usar. 2.4.1. PEER DISCOVERY PROTOCOL (PDP)

El Protocolo de Descubrimiento de Nodos (PDP) se usa para descubrir cualquier recurso de nodo publicado. Los recursos son representados como advertisements. Un recurso puede ser un nodo, grupo de nodos, tubería, servicio, o cualquier otro recurso que tenga advertisement.

PDP permite a un nodo encontrar advertisements en otros nodos. El PDP es el protocolo predeterminado de descubrimiento para todo usuario que cree un grupo o use el Net Peer Group predeterminado. Servicios personalizados de descubrimiento pueden escogerse para ampliar el PDP. Si un grupo de nodos no tiene su propio servicio de descubrimiento, entonces el PDP se usa para sondear los nodos en busca de advertisements.

Hay múltiples formas para descubrir información distribuida. La plataforma JXTA J2SE actual usa una combinación de IP multicast para la subred local y el uso de nodos de encuentro, una técnica basada en “serpentear por la red” (network crawling). Los nodos de encuentro proporcionan el mecanismo para enviar peticiones de un nodo conocido al siguiente para descubrir información dinámicamente.

Un nodo puede ser preconfigurado con un conjunto predefinido de nodos de encuentro. Un

nodo también puede escoger inicializarse dinámicamente localizando a nodos de encuentro o recursos de la red en su entorno cercano. También pueden agregarse otras técnicas, tales como redes de contenido direccionable (CAN's) para realizar descubrimiento de recursos.

Los nodos generan peticiones de mensajes de consulta de descubrimiento para descubrir

advertisements dentro de un grupo de nodos. Este mensaje contiene la credencial del grupo del nodo emisor, identificándolo para el destinatario del mensaje. Los mensajes pueden enviarse a

JXTA

JXTA sobre J2ME - 21 -

cualquier nodo dentro de un grupo o a un nodo de encuentro. El mensaje de respuesta devuelve uno o más advertisements. 2.4.2. PEER INFORMATION PROTOCOL (PIP)

Una vez que un nodo está ubicado, sus capacidades y estado pueden ser consultados. El Protocolo de Información de Nodo (PIP) provee un conjunto de mensajes para obtener información de estado del nodo. Esta información puede usarse para la implementación comercial o interna de aplicaciones JXTA. Por ejemplo, en las implementaciones comerciales la información puede usarse para determinar el uso de un servicio de nodo y facturar a los consumidores del servicio por su uso. En una implementación interna, la información puede ser usada para supervisar el comportamiento de un nodo y cambiar el itinerario del tráfico de la red para mejorar el funcionamiento global.

El mensaje ping de PIP se envía a un nodo para comprobar si el nodo está vivo y obtener información sobre él. El mensaje ping especifica si se debe devolver una respuesta total (advertisement del nodo) o un reconocimiento simple (si está encendido y el tiempo activo).

El mensaje de información de nodo se usa para enviar un mensaje en respuesta a un mensaje

ping. Contiene la credencial del emisor, el identificador del nodo emisor y el del nodo objetivo, el tiempo activo, y el advertisement de nodo.

2.4.3. PEER RESOLVER PROTOCOL (PRP)

El Protocolo de Resolución de Nodo (PRP) permite a los nodos enviar peticiones genéricas de consulta a otros nodos e identifica las respuestas encontradas. Las peticiones de consulta pueden enviarse a un nodo específico, o pueden ser propagadas por el Servicio Rendezvous (usando nodos de encuentro) dentro del alcance de un grupo de nodos. El PRP usa el Servicio Rendezvous para propagar una consulta a múltiples nodos y usa mensajes unicast para enviar consultas a nodos determinados.

PRP es un protocolo fundamental que da soporte a peticiones genéricas de consulta. Tanto PIP como PDP se construyen usando PRP, y proporcionan consultas/peticiones específicas. PIP se usa para consultar información específica de estado y PDP se usa para descubrir recursos de nodo. PRP puede servir para cualquier consulta genérica que pueda necesitar una aplicación. Por ejemplo, el PRP permite a los nodos definir e intercambiar consultas para encontrar o buscar información de servicio como el estado del servicio, el estado de un extremo de una tubería, etc.

El mensaje de consulta de resolución se usa para enviar una petición de consulta de

resolución a un servicio en otro miembro de un grupo de nodos. Contiene la credencial del emisor, un identificador único de consulta, un manipulador de servicio específico y la consulta en sí. Cada servicio puede registrar un manipulador en el servicio de resolución del grupo de nodos para procesar las peticiones de consulta de resolución y generar respuestas.

El mensaje de respuesta de resolución se usa para enviar un mensaje en respuesta a un

mensaje de consulta de resolución. Contiene la credencial del emisor, un identificador único de búsqueda, un manipulador de servicio específico y la respuesta. Se pueden enviar múltiples

JXTA

JXTA sobre J2ME - 22 -

mensajes de consulta de resolución, pudiéndose recibir ninguna, una o más respuestas para una petición de consulta.

Los nodos también pueden participar en el índice distribuido de recursos compartidos (SRDI). SRDI proporciona un mecanismo genérico donde los servicios JXTA pueden utilizar índices distribuidos de recursos compartidos con otros nodos. Estos índices se usan para dirigir consultas en la dirección donde la consulta es más probable que sea contestada, y repropagar mensajes a los nodos interesados en ésta propagación de mensajes. PRP envía a un mensaje SRDI de resolución al manipulador nombrado en uno o más nodos en el grupo de nodos. El mensaje SRDI de resolución se envía a un manipulador específico, y contiene una cadena que se interpretará por el manipulador apuntado. 2.4.4. PIPE BINDING PROTOCOL (PBP)

El Protocolo de Asociación de Tuberías (PBP) es usado por los miembros del grupo de nodos para vincular un advertisement de una tubería al extremo de una tubería.

Una tubería puede ser vista como una cola mensajes que soporta las operaciones de crear,

abrir/resolver (asociar), cerrar (desligar), suprimir, enviar y recibir. Las implementaciones efectivas de una tubería pueden diferir, pero todas usan PBP para asociar la tubería a un punto final. Durante la operación abstracta de creación, un nodo local asocia un extremo de tubería a una tubería de transporte.

El mensaje de consulta PBP se envía por un extremo de una tubería para encontrar otro extremo de tubería ligado al mismo advertisement de tubería. El mensaje de consulta puede pedir información que no ha sido obtenida de la memoria caché. Con esto actualizamos la información sobre los nodos. El mensaje de consulta también puede contener a un identificador de nodo optativo, que si está presente indica que sólo el nodo especificado debe responder a la consulta.

Un mensaje de respuesta PBP se devuelve al nodo que hizo la petición por cada nodo ligado a la tubería. El mensaje contiene el identificador de tubería, el nodo donde la entrada de tubería correspondiente haya sido creada, y un valor booleano indicando si la entrada de tubería existe en el nodo especificado.

2.4.5. ENDPOINT ROUTING PROTOCOL (ERP)

El Protocolo de Encaminamiento de Puntos finales (ERP) define un conjunto de mensajes de petición/consulta que se usan para encontrar información de encaminamiento. Esta información de la ruta es necesaria para enviar a un mensaje de un nodo (la fuente) a otro (el destino).

Cuando un nodo recibe instrucciones para enviar un mensaje a una dirección de un punto final de nodo dado, primero mira en su memoria caché local para determinar si tiene una ruta para este nodo. Si no encuentra una ruta, entonces envía una petición de consulta de resolución de ruta a los nodos de retransmisión disponibles pidiendo información de la ruta. Cuando un nodo de retransmisión recibe una consulta de ruta, éste comprueba si conoce la ruta. Si esto ocurre, entonces devuelve la información de ruta como una enumeración de saltos.

JXTA

JXTA sobre J2ME - 23 -

Cualquier nodo puede consultar a un nodo de retransmisión para obtener la información de la ruta, y cualquier nodo en un grupo de nodos puede convertirse en uno de retransmisión.

La información de la ruta incluye el identificador de nodo de la fuente, el identificador de nodo del destino, un tiempo de vida (TTL) para la ruta, y una secuencia ordenada de identificadores de nodo de las puertas de enlace (gateways). La secuencia de los identificadores de nodo podría no estar completa, pero debería contener al menos al primer nodo intermedio.

Las peticiones de consulta de la ruta son enviadas por un nodo a un nodo de retransmisión para solicitar información de la ruta. La búsqueda puede indicar una preferencia para buscar dinámicamente una ruta nueva.

Los mensajes de respuesta de ruta se envían por un nodo de retransmisión en respuesta a peticiones de información de ruta. Este mensaje contiene el identificador de nodo del destino, el identificador de nodo y el advertisement de nodo del encaminador que conoce una ruta para el destino y una secuencia ordenada de uno o más nodos de retransmisión.

2.4.6. RENDEZVOUS PROTOCOL (RVP)

El Protocolo de Rendezvous (RVP) es el responsable de propagar los mensajes dentro de un grupo de nodos. Mientras que grupos de nodos diferentes pueden tener diferentes métodos para propagar los mensajes, el Protocolo Rendezvous define un protocolo simple que permite:

• A los nodos conectarse al servicio (puede propagar mensajes y recibir mensajes

propagados). • Controlar la propagación del mensaje (el tiempo de vida, la detección de bucles, etc.).

RVP es usado por el Protocolo de Resolución de Nodo y por el Protocolo de Asociación de

Tuberías para propagar los mensajes.

J2ME

JXTA sobre J2ME - 24 -

CAPÍTULO 3º: J2ME 3.1. INTRODUCCIÓN

Es de sobra conocido por todos el éxito que ha alcanzado el lenguaje de programación Java, llegando a ser el lenguaje más popular en Internet. Fue creado por Sun Microsystems a principios de los 90, procediendo su nombre de como se conoce popularmente al café de alta calidad en EE.UU., de ahí que su logotipo sea un taza de dicha bebida.

Los constructores de Java optaron en su momento por dotar al lenguaje de un conjunto rico

de capacidades orientadas a objetos que permitiese añadir librerías de apoyo a medida que fuera necesario y en función de las necesidades tecnológicas del momento. Es por ello que Java supone un núcleo en torno al cuál giran una multitud de bibliotecas con las más diversas orientaciones: desde bibliotecas dedicadas a la gestión de interfaces de usuario hasta aquéllas que se dedican al tratamiento de imágenes bidimensionales, pasando por las que se centran en difundir información multimedia, envío y recepción de correo electrónico, distribución de componentes software en Internet, etc.

Por otra parte, en los últimos años, es notorio el espectacular incremento en el uso de dispositivos de pequeña capacidad con acceso a redes de información, principalmente teléfonos móviles. Este gran número de usuarios ha conllevado la necesidad de crear software específico para dicho tipo de terminales, incluidos programas desarrollados en Java.

Las características concretas de este tipo de dispositivos han obligado a los desarrolladores

de Java a construir un subconjunto del lenguaje y a reconfigurar sus principales bibliotecas para permitir su adaptación a un entorno con poca capacidad de memoria, poca velocidad de proceso, y pantallas de reducidas dimensiones. Todo esto hace que necesitemos una nueva plataforma de desarrollo y ejecución: la Java 2 Micro Edition, o de forma abreviada J2ME.

La edición Java 2 Micro Edition fue presentada en 1999 por Sun Microsystems con el

propósito de habilitar aplicaciones Java para pequeños dispositivos. En esta presentación, lo que realmente se enseñó fue una primera versión de una nueva Java Virtual Machine (JVM) que podía ejecutarse en dispositivos Palm. Posteriormente se ha ido desarrollando hasta el punto de que, actualmente, la totalidad de compañías telefónicas y fabricantes de móviles están implantando los protocolos y dispositivos necesarios para soportarla. 3.2. CARACTERÍSTICAS

Sun, dispuesto a proporcionar las herramientas necesarias para cubrir las necesidades de todos los usuarios, creó distintas versiones de Java de acuerdo a las necesidades de cada uno. Según esto nos encontramos con que el paquete Java 2 lo podemos dividir en tres ediciones distintas: J2SE (Java Standard Edition) orientada al desarrollo de aplicaciones independientes de la plataforma, J2EE (Java Enterprise Edition) orientada al entorno empresarial y J2ME (Java

J2ME

JXTA sobre J2ME - 25 -

Micro Edition) orientada a dispositivos con capacidades restringidas. Estas tres ediciones las vemos representadas en la siguiente figura:

Figura 3.1: Arquitectura de la plataforma Java 2 de Sun

La versión J2ME está enfocada a la aplicación de la tecnología Java en dispositivos

electrónicos con capacidades computacionales y gráficas muy reducidas, tales como teléfonos móviles, PDAs o electrodomésticos inteligentes. Esta edición tiene unos componentes básicos que la diferencian de las otras versiones, como el uso de una máquina virtual denominada KVM (Kilo Virtual Machine, debido a que requiere sólo unos pocos Kilobytes de memoria para funcionar) en vez del uso de la JVM clásica y la inclusión de un pequeño y rápido recolector de basura.

Asimismo, J2ME contiene una mínima parte de las APIs de Java. Esto es debido a que la

edición estándar de APIs de Java ocupa 20 Mb, y los dispositivos pequeños disponen de una cantidad de memoria mucho más reducida. En concreto, J2ME usa 37 clases de la plataforma J2SE provenientes de los paquetes java.lang, java.io, java.util. Esta parte de la API que se mantiene fija forma parte de lo que se denomina “configuración”, concepto que pasaremos a explicar más adelante.

Como vemos, J2ME representa una versión simplificada de J2SE. Sun separó estas dos versiones ya que J2ME estaba pensada para dispositivos con limitaciones de proceso y capacidad gráfica. También separó J2SE de J2EE porque este último exigía unas características muy pesadas o especializadas de E/S, trabajo en red, etc. Por tanto, separó ambos productos por razones de eficiencia. Hoy, J2EE es un superconjunto de J2SE pues contiene toda la funcionalidad de éste y más características, así como J2ME es un subconjunto de J2SE (excepto por el paquete javax.microedition) ya que, como se ha mencionado, contiene varias limitaciones con respecto a J2SE.

J2ME

JXTA sobre J2ME - 26 -

Figura 3.2: Relación entre las APIs de la plataforma Java

3.3. COMPONENTES

A continuación vamos a ver cuáles son los componentes que forman parte de esta tecnología:

• Por un lado tenemos una serie de máquinas virtuales Java con diferentes requisitos, cada

una para diferentes tipos de pequeños dispositivos. • Configuraciones, que son un conjunto de clases básicas orientadas a conformar el

corazón de las implementaciones para dispositivos de características específicas. Existen 2 configuraciones definidas en J2ME: Connected Limited Device Configuration (CLDC) enfocada a dispositivos con restricciones de procesamiento y memoria, y Connected Device Configuration (CDC) enfocada a dispositivos con más recursos.

• Perfiles, que son unas bibliotecas Java de clases específicas orientadas a implementar funcionalidades de más alto nivel para familias específicas de dispositivos.

• Una serie de paquetes opcionales. La arquitectura de un entorno de ejecución la podemos ver en la Figura 3.3:

Figura 3.3: Entorno de ejecución

J2ME

JXTA sobre J2ME - 27 -

3.3.1. MÁQUINAS VITUALES J2ME

Una máquina virtual de Java (JVM) es un programa encargado de interpretar el código intermedio (bytecode) de los programas Java precompilados, pasándolo a código máquina ejecutable por la plataforma, efectuar las llamadas pertinentes al sistema operativo subyacente y observar las reglas de seguridad y corrección de código definidas para el lenguaje Java. De esta forma, la JVM proporciona al programa Java independencia de la plataforma con respecto al hardware y al sistema operativo.

Las implementaciones tradicionales de JVM son, en general, muy pesadas en cuanto a

memoria ocupada y requerimientos computacionales. J2ME define varias JVMs adecuadas al ámbito de los dispositivos electrónicos que, en algunos casos, suprimen algunas características con el fin de obtener una implementación menos exigente.

Ya hemos visto que existen dos configuraciones CLDC y CDC, cada una con unas

características propias que veremos más adelante. Como consecuencia, cada una requiere su propia máquina virtual. La VM (Virtual Machine) de la configuración CLDC se denomina KVM y la de la configuración CDC se denomina CVM. Veremos a continuación las características principales de cada una de ellas.

• KVM: se corresponde con la Máquina Virtual más pequeña desarrollada por Sun. Su nombre KVM proviene de Kilobyte (haciendo referencia a la baja ocupación de memoria, entre 40Kb y 80Kb). Se trata de una implementación de Máquina Virtual reducida y especialmente orientada a dispositivos con bajas capacidades computacionales y de memoria. La KVM está escrita en lenguaje C, aproximadamente unas 24000 líneas de código, y fue diseñada para ser:

o Pequeña, con una carga de memoria entre los 40Kb y los 80 Kb, dependiendo de la plataforma y las opciones de compilación.

o Alta portabilidad. o Modulable. o Lo más completa y rápida posible y sin sacrificar características para las que fue

diseñada. Sin embargo, esta baja ocupación de memoria hace que posea algunas limitaciones

con respecto a la clásica Java Virtual Machine (JVM): 1) No hay soporte para tipos en coma flotante. No existen por tanto los tipos double

ni float. Esta limitación está presente porque los dispositivos carecen del hardware necesario para estas operaciones.

2) No existe soporte para JNI (Java Native Interface) debido a los recursos limitados de memoria.

3) No existen cargadores de clases (class loaders) definidos por el usuario. Sólo existen los predefinidos.

4) No se permiten los grupos de hilos o hilos daemon. Cuándo queramos utilizar grupos de hilos utilizaremos los objetos Colección para almacenar cada hilo en el ámbito de la aplicación.

5) No existe la finalización de instancias de clases. No existe el método Object.finalize().

J2ME

JXTA sobre J2ME - 28 -

6) No hay referencias débiles para la eliminación del objeto por el recolector de basuras.

7) Limitada capacidad para el manejo de excepciones debido a que el manejo de éstas depende en gran parte de las APIs de cada dispositivo por lo que son éstos los que controlan la mayoría de las excepciones.

8) No soporta reflexión, es decir, el mecanismo por el que los objetos pueden obtener información sobre otros objetos en tiempo de ejecución.

Aparte de la no inclusión de estas características, el verificador de clases estándar de Java es demasiado grande para la KVM. De hecho es más grande que la propia KVM y el consumo de memoria es excesivo, más de 100Kb para las aplicaciones típicas. Este verificador de clases es el encargado de rechazar las clases no válidas en tiempo de ejecución. Por esta razón los dispositivos que usen la configuración CLDC y KVM introducen un algoritmo de verificación de clases en dos pasos. Este proceso podemos verlo en la Figura 3.4:

Figura 3.4: Preverificación de clases en CDLC/KVM

La KVM puede ser compilada y probada en tres plataformas distintas: 1) Solaris Operating Environment. 2) Windows. 3) PalmOs.

• CVM: la CVM (Compact Virtual Machine) ha sido tomada como Máquina Virtual Java

de referencia para la configuración CDC y soporta las mismas características que la Máquina Virtual de J2SE. Está orientada a dispositivos electrónicos con procesadores de 32 bits de gama alta y en torno a 2Mb o más de memoria RAM. Las características que presenta esta Máquina Virtual son:

1) Sistema de memoria avanzado. 2) Tiempo de espera bajo para el recolector de basura. 3) Separación completa de la VM del sistema de memoria.

J2ME

JXTA sobre J2ME - 29 -

4) Recolector de basura modularizado. 5) Portabilidad. 6) Rápida sincronización. 7) Ejecución de las clases Java fuera de la memoria de sólo lectura (ROM). 8) Soporte nativo de hilos. 9) Baja ocupación en memoria de las clases. 10) Proporciona soporte e interfaces para servicios en Sistemas Operativos de Tiempo Real. 11) Conversión de hilos Java a hilos nativos. 12) Soporte para todas las características de Java2 v1.3 y librerías de seguridad, referencias débiles, Interfaz Nativa de Java (JNI), invocación remota de métodos (RMI), Interfaz de depuración de la Máquina Virtual (JVMDI).

3.3.2. CONFIGURACIONES

Una configuración es el conjunto mínimo de APIs Java que permiten desarrollar aplicaciones para un grupo de dispositivos. Estas APIs describen las características básicas, comunes a todos los dispositivos:

• Características soportadas del lenguaje de programación Java. • Características soportadas por la Máquina Virtual Java. • Bibliotecas básicas de Java y APIs soportadas.

Como ya hemos visto con anterioridad, existen dos configuraciones en J2ME: CLDC,

orientada a dispositivos con limitaciones computacionales y de memoria y CDC, orientada a dispositivos con no tantas limitaciones. Ahora veremos un poco más en profundidad cada una de estas configuraciones. 3.3.2.1. Configuración de dispositivos con conexión,CDC (Connected Device Configuration)

La CDC está orientada a dispositivos con cierta capacidad computacional y de memoria. Por

ejemplo, decodificadores de televisión digital, televisores con Internet, algunos electrodomésticos y sistemas de navegación en automóviles. CDC usa una Máquina Virtual Java similar en sus características a una de J2SE, pero con limitaciones en el apartado gráfico y de memoria del dispositivo. Ésta Máquina Virtual es la que hemos visto como CVM (Compact Virtual Machine). La CDC está enfocada a dispositivos con las siguientes capacidades:

• Procesador de 32 bits. • Disponer de 2 Mb o más de memoria total, incluyendo memoria RAM y ROM. • Poseer la funcionalidad completa de la Máquina Virtual Java2. • Conectividad a algún tipo de red.

La CDC está basada en J2SE v1.3 e incluye varios paquetes Java de la edición estándar. Las

peculiaridades de la CDC están contenidas principalmente en el paquete javax.microedition.io,

J2ME

JXTA sobre J2ME - 30 -

que incluye soporte para comunicaciones HTTP y basadas en datagramas. La Tabla 3.1 nos muestra las librerías incluidas en CDC:

Tabla 3.1: Librerías de configuración CDC

3.3.2.2. Configuración de dispositivos limitados con conexión, CLDC (Connected

Limited Device Configuration)

La CLDC está orientada a dispositivos dotados de conexión y con limitaciones en cuanto a capacidad gráfica, cómputo y memoria. Un ejemplo de estos dispositivos son: teléfonos móviles, buscapersonas (pagers), PDAs, organizadores personales, etc.

Algunas de estas restricciones vienen dadas por el uso de la KVM, necesaria al trabajar con la CLDC debido a su pequeño tamaño. Los dispositivos que usan CLDC deben cumplir los siguientes requisitos:

• Disponer entre 160 Kb y 512 Kb de memoria total disponible. Como mínimo se debe disponer de 128 Kb de memoria no volátil para la Máquina Virtual Java y las bibliotecas CLDC, y 32 Kb de memoria volátil para la Máquina Virtual en tiempo de ejecución.

• Procesador de 16 o 32 bits con al menos 25 Mhz de velocidad. • Ofrecer bajo consumo, debido a que estos dispositivos trabajan con suministro de energía

limitado, normalmente baterías. • Tener conexión a algún tipo de red, normalmente sin cable, con conexión intermitente y

ancho de banda limitado (unos 9600 bps).

La CLDC aporta las siguientes funcionalidades a los dispositivos: • Un subconjunto del lenguaje Java y todas las restricciones de su Máquina Virtual (KVM). • Un subconjunto de las bibliotecas Java del núcleo. • Soporte para E/S básica. • Soporte para acceso a redes. • Seguridad.

J2ME

JXTA sobre J2ME - 31 -

La Tabla 3.2 nos muestra las librerías incluidas en la CLDC:

Tabla 3.2: Librerías de configuración CLDC

En cualquier caso, una determinada Configuración no se encarga del mantenimiento del ciclo

de vida de la aplicación, interfaces de usuario o manejo de eventos, sino que estas responsabilidades caen en manos de los perfiles. 3.3.3. PERFILES

Como acabamos de comentar, el perfil es el que define las APIs que controlan el ciclo de vida de la aplicación, interfaz de usuario, etc. Más concretamente, un perfil es un conjunto de APIs orientado a un ámbito de aplicación determinado. Los perfiles identifican un grupo de dispositivos por la funcionalidad que proporcionan (electrodomésticos, teléfonos móviles, etc.) y el tipo de aplicaciones que se ejecutarán en ellos. Las librerías de la interfaz gráfica son un componente muy importante en la definición de un perfil. Aquí nos podemos encontrar grandes diferencias entre interfaces, desde el menú textual de los teléfonos móviles hasta los táctiles de los PDAs.

El perfil establece unas APIs que definen las características de un dispositivo, mientras que la configuración hace lo propio con una familia de ellos. Esto hace que a la hora de construir una aplicación se cuente tanto con las APIs del perfil como de la configuración. Tenemos que tener en cuenta que un perfil siempre se construye sobre una configuración determinada. De este modo, podemos pensar en un perfil como un conjunto de APIs que dotan a una configuración de funcionalidad específica.

Existen unos perfiles que construiremos sobre la configuración CDC y otros que construiremos sobre la CLDC. Para la configuración CDC tenemos los siguientes perfiles:

• Foundation Profile. • Personal Profile. • RMI Profile.

Para la configuración CLDC tenemos los siguientes: • PDA Profile. • Mobile Information Device Profile (MIDP).

En la Figura 3.5 se puede ver como quedaría el esquema del entorno de ejecución al

completo. Un perfil puede ser construido sobre cualquier otro. Sin embargo, una plataforma J2ME sólo puede contener una configuración.

J2ME

JXTA sobre J2ME - 32 -

A continuación describiremos brevemente cada uno de los perfiles anteriormente mencionados.

Figura 3.5: Arquitectura del entorno de ejecución de J2ME

• Foundation Profile: este perfil define una serie de APIs sobre la CDC orientadas a

dispositivos que carecen de interfaz gráfica como, por ejemplo, decodificadores de televisión digital. Este perfil incluye gran parte de los paquetes de la J2SE, pero excluye totalmente los paquetes “java.awt” Abstract Windows Toolkit (AWT) y “java.swing” que conforman la interfaz gráfica de usuario (GUI) de J2SE. Si una aplicación requiriera una GUI, entonces sería necesario un perfil adicional. Los paquetes que forman parte del Foundation Profile se muestran en la Tabla 3.3.

Tabla 3.3: Librerías del Foundation Profile

• Personal Profile: el Personal Profile es un subconjunto de la plataforma J2SE v1.3, y proporciona un entorno con un completo soporte gráfico AWT. El objetivo es el de dotar a la configuración CDC de una interfaz gráfica completa, con capacidades web y soporte de applets Java. Este perfil requiere una implementación del Foundation Profile. La Tabla 3.4 nos muestra los paquetes que conforman el Personal Profile v1.0.

J2ME

JXTA sobre J2ME - 33 -

Tabla 3.4: Librerías del Personal Profile

• RMI Profile: este perfil requiere una implementación del Foundation Profile. El perfil RMI soporta un subconjunto de las APIs J2SE v1.3 RMI. Algunas características de estas APIs se han eliminado del perfil RMI debido a las limitaciones de cómputo y memoria de los dispositivos:

o Java.rmi.server.disableHTTP. o Java.rmi.activation.port. o Java.rmi.loader.packagePrefix. o Java.rmi.registry.packagePrefix. o Java.rmi.server.packagePrefix.

• PDA Profile: el PDA Profile está construido sobre CLDC. Pretende abarcar PDAs de

gama baja, tipo Palm, con una pantalla y algún tipo de puntero (ratón o lápiz) y una resolución de al menos 20000 pixels (al menos 200x100 pixels) con un factor 2:1. Aún se encuentra en fase de definición.

• Mobile Information Device Profile (MIDP): este perfil está construido sobre la

configuración CLDC. Al igual que CLDC fue la primera configuración definida para J2ME, MIDP fue el primer perfil definido para esta plataforma. Este perfil está orientado para dispositivos con las siguientes características:

o Reducida capacidad computacional y de memoria. o Conectividad limitada (en torno a 9600 bps). o Capacidad gráfica muy reducida (mínimo un display de 96x54 pixels monocromo). o Entrada de datos alfanumérica reducida. o 128 Kb de memoria no volátil para componentes MIDP. o 8 Kb de memoria no volátil para datos persistentes de aplicaciones.

J2ME

JXTA sobre J2ME - 34 -

o 32 Kb de memoria volátil en tiempo de ejecución para la pila Java. Los tipos de dispositivos que se adaptan a estas características son: teléfonos móviles, buscapersonas (pagers) o PDAs de gama baja con conectividad. El perfil MIDP establece las capacidades del dispositivo, por lo tanto, especifica las APIs relacionadas con:

o La aplicación (semántica y control de la aplicación MIDP). o Interfaz de usuario. o Almacenamiento persistente. o Trabajo en red. o Temporizadores.

En la Tabla 3.5 podemos ver cuáles son los paquetes que están incluidos en el perfil MIDP:

Tabla 3.5: Librerías del perfil MIDP

Desde un punto de vista práctico, MIDP es el único perfil actualmente disponible. Las aplicaciones que realizamos utilizando MIDP reciben el nombre de MIDlets (por similitud con APPlets). Decimos así que un MIDlet es una aplicación Java realizada con el perfil MIDP sobre la configuración CLDC. Para el desarrollo de MIDlets existen varias herramientas que proporcionan un entorno con el que se puede emular el comportamiento del terminal móvil. Entre otras, se encuentran el Sun One Studio Mobile

Edition y el J2ME Wíreles Toolkit.

3.4. MIDLETS

De aquí en adelante vamos a centrarnos en el perfil MIDP, al ser éste el que utilizan los

teléfonos móviles y al estar pensada la aplicación objeto de estudio para estos dispositivos.

En este apartado hablaremos del gestor de aplicaciones que es el software de que dispone el móvil para gestionar el uso de los MIDlets. También vamos a hablar sobre las propiedades de los MIDlets, su ciclo de vida y los estados por los que pasa, nombraremos las clases que contiene el paquete javax.microedition.midlet y por último veremos su estructura típica.

J2ME

JXTA sobre J2ME - 35 -

Los MIDlets son aplicaciones creadas usando la especificación MIDP. Están diseñados para ser ejecutados, como ya sabemos, en dispositivos con poca capacidad gráfica, de cómputo y de memoria. En estos dispositivos no disponemos de líneas de comandos donde poder ejecutar las aplicaciones que queramos, si no que reside en él un software que es el encargado de ejecutar los MIDlets y gestionar los recursos que éstos ocupan. En el siguiente apartado vamos a hablar de este software conocido como gestor de aplicaciones. 3.4.1. GESTOR DE APLICACIONES (AMS)

El gestor de aplicaciones o AMS (Application Management System) es el software encargado de gestionar los MIDlets. Este software reside en el dispositivo y es el que nos permite ejecutar, pausar o destruir nuestras aplicaciones J2ME. El AMS realiza dos grandes funciones:

• Por un lado gestiona el ciclo de vida de los MIDlets. • Por otro, es el encargado de controlar los estados por los que pasa el MIDlet mientras está

en la memoria del dispositivo, es decir, en ejecución. 3.4.1.1. Ciclo de vida de un MIDlet

El ciclo de vida de un MIDlet pasa por cinco fases (ver Figura 3.6): descubrimiento, instalación, ejecución, actualización y borrado.

Figura 3.6: Ciclo de vida de un MIDlet

El AMS es el encargado de gestionar cada una de estas fases de la siguiente manera:

1. Descubrimiento: esta fase es la etapa previa a la instalación del MIDlet y es dónde seleccionamos (usando un software conocido como discovery application o DA) la aplicación a descargar. El gestor de aplicaciones nos tiene que proporcionar los mecanismos necesarios para realizar la descarga del MIDlet elegido. Esto se puede realizar de distintas maneras, dependiendo de las capacidades del dispositivo. Por ejemplo, mediante un cable conectado a un ordenador o mediante una conexión

J2ME

JXTA sobre J2ME - 36 -

inalámbrica (OTA por ejemplo). Los MIDlets se suelen encontrar separados en dos ficheros: uno con extensión JAD llamado descriptor, que contiene información sobre el MIDlet y es el que hay que localizar para iniciar la descarga, y otro con extensión JAR que contiene las clases y demás componentes del MIDlet, que nos lo descargamos a partir de la dirección indicada en un campo del descriptor. Para la MIDP 1.0 la URL sólo puede apuntar a un descriptor JAD mientras que en la MIDP 2.0 puede contener un JAD o un archivo JAR. Si se trata de un JAR, se descarga este archivo directamente. Cuando se descarga el descriptor JAD, se utiliza la URL que contiene dicho archivo para descargarse el JAR asociado. Aunque en la MIDP 2.0 se puede descargar directamente el JAR, es recomendable descargar primero el JAD ya que nos permite determinar si el móvil cuenta con recursos suficientes para ejecutar la aplicación. 2. Instalación: una vez descargado el MIDlet en el dispositivo, comienza el proceso de instalación. En esta fase el gestor de aplicaciones controla todo el proceso informando al usuario tanto de la evolución de la instalación como de si existiese algún problema durante ésta. Cuándo un MIDlet está instalado en el dispositivo, todas sus clases, archivos y almacenamiento persistente están preparados y listos para su uso. 3. Ejecución: mediante el gestor de aplicaciones vamos a ser capaces de iniciar la ejecución de los MIDlets. En esta fase, el AMS tiene la función de gestionar los estados del MIDlet en función de los eventos que se produzcan durante esta ejecución.

4. Actualización: el AMS tiene que ser capaz de detectar después de una descarga si el MIDlet descargado es una actualización de un MIDlet ya presente en el dispositivo. Si es así, nos tiene que informar de ello, además de darnos la oportunidad de decidir si queremos realizar la actualización pertinente o no. También debe preservar los contenidos del sistema de almacenamiento persistente (RMS). MIDP 2.0 añade ciertas comprobaciones de seguridad para evitar accesos indeseados al RMS: tanto el JAD como el JAR deben provenir de la misma fuente. Para los MIDlets con firma, ambos deben tener también el mismo firmante. Y no se permitirá que un MIDlet sin forma sustituya a uno firmado. 5. Borrado: en esta fase el AMS es el encargado de borrar el MIDlet seleccionado del dispositivo. El AMS nos pedirá confirmación antes de proceder a su borrado y nos informará de cualquier circunstancia que se produzca.

Hay que indicar que el MIDlet puede permanecer en el dispositivo todo el tiempo que

queramos. Después de la fase de instalación, el MIDlet queda almacenado en una zona de memoria persistente del dispositivo MID. El usuario de éste dispositivo es el encargado de decidir en qué momento quiere eliminar la aplicación y así se lo hará saber al AMS mediante alguna opción que éste nos suministre.

3.4.1.2. Estados de un MIDlet

Además de gestionar el ciclo de vida de los MIDlets, como ya hemos visto, el AMS es el encargado de controlar los estados del MIDlet durante su ejecución. Durante ésta, el MIDlet es

J2ME

JXTA sobre J2ME - 37 -

cargado en la memoria del dispositivo y es aquí donde puede transitar entre tres estados diferentes: activo, en pausa y destruido.

Cuando un MIDlet comienza su ejecución, está en el estado “Activo” pasando a estado de

“Pausa”, por ejemplo, si durante su ejecución recibimos una llamada o un mensaje. El gestor de aplicaciones debe ser capaz de cambiar el estado de la aplicación en función de los eventos externos al ámbito de ejecución de la aplicación que se vayan produciendo. En este caso, el gestor de aplicaciones interrumpiría la ejecución del MIDlet sin que se viese afectada la ejecución de éste y lo pasaría al estado de “Pausa” para atender la llamada o leer el mensaje. En este estado el MIDlet no debe usar ningún recurso compartido. Para volver a pasar a ejecución tiene que cambiar su estado a “Activo”. Una vez que terminemos de trabajar con el MIDlet y salgamos de él, éste pasaría al estado de “Destruido” dónde sería eliminado de la memoria volátil del dispositivo que es usada para la ejecución de aplicaciones. Desde aquí no se puede transitar a otro estado. Además se liberan todos los recursos ocupados por el MIDlet. La Figura 3.7 nos muestra el diagrama de estados de un MIDlet en ejecución:

Figura 3.7: Estados de un MIDlet Como vemos en el diagrama, un MIDlet puede cambiar de estado mediante una llamada a

los métodos MIDlet.startApp(), MIDlet.pauseApp() o MIDlet.destroyApp(). El gestor de aplicaciones cambia el estado de los MIDlets haciendo una llamada a cualquiera de los métodos anteriores. A su vez un MIDlet también puede cambiar de estado por sí mismo.

Durante una ejecución típica, en primer lugar, se realiza la llamada al constructor del MIDlet

pasando éste al estado de “Pausa” durante un corto período de tiempo. El AMS por su parte crea una nueva instancia del MIDlet. Cuándo el dispositivo está preparado para ejecutar el MIDlet, el AMS invoca al método MIDlet.startApp() para entrar en el estado de “Activo”. El MIDlet entonces, ocupa todos los recursos que necesita para su ejecución. Durante este estado, el MIDlet puede pasar al estado de “Pausa” por una acción del usuario, o bien, por el AMS, que reduciría en todo lo posible el uso de los recursos del dispositivo por parte del MIDlet. Tanto en el estado “Activo” como en el de “Pausa”, el MIDlet puede pasar al estado “Destruido” realizando una llamada al método MIDlet.destroyApp(). Esto puede ocurrir porque el MIDlet haya finalizado su ejecución o porque una aplicación prioritaria necesite ser ejecutada en memoria en lugar del MIDlet. Una vez destruido el MIDlet, libera todos los recursos ocupados.

J2ME

JXTA sobre J2ME - 38 -

3.4.2. EL PAQUETE JAVAX.MICROEDITION.MIDLET

El paquete javax.microedition.midlet define las aplicaciones MIDP y su comportamiento con respecto al entorno de ejecución. Como ya sabemos, una aplicación creada usando MIDP es un MIDlet. En la Tabla 3.6 vemos cuáles son las clases que están incluidas en este paquete:

Tabla 3.6: Clases del paquete javax.microedition.midlet

3.4.2.1. Clase MIDlet public abstract class MIDlet

Un MIDlet es una aplicación realizada usando el perfil MIDP como ya sabemos. La aplicación debe extender a esta clase para que el AMS pueda gestionar sus estados y tener acceso a sus propiedades. El MIDlet puede por sí mismo realizar cambios de estado invocando a los métodos apropiados. Los métodos de los que dispone esta clase son los siguientes:

• protected MIDlet() Constructor de clase sin argumentos. Si la llamada a este constructor falla, se lanzaría la excepción SecurityException.

• public final int checkPermission(String permiso) Consigue el estado del permiso especificado. Este permiso está descrito en el atributo MIDlet-Permission del archivo JAD.

• protected abstract void destroyApp(boolean incondicional) throws MIDletstateChangeException Indica la terminación del MIDlet y su paso al estado de “Destruido”. Este método puede ser llamado desde los estados “Pausa” o “Activo” y se liberarán todos los recursos. Si el parámetro ‘incondicional’ es false, el MIDlet puede lanzar la excepción MIDletstateChangeException para indicar que no puede ser destruido en este momento. Si es true, el MIDlet asume su estado de destruido independientemente de como finalice el método.

• public final String getAppProperty(String key) Este método proporciona al MIDlet un mecanismo que le permite recuperar el valor de las propiedades desde el AMS. Las propiedades se consiguen por medio de los archivos manifest y JAD.

• public final void notifyDestroyed() Este método es utilizado por un MIDlet para indicar al AMS que ha entrado en el estado de “Destruido”. En este caso, todos los recursos ocupados por el MIDlet deben ser liberados por éste de la misma forma que si se hubiera llamado al método MIDlet.destroyApp(). El AMS considerará que todos los recursos que ocupaba el MIDlet están libres para su uso.

• public final void notifyPaused()

J2ME

JXTA sobre J2ME - 39 -

Se notifica al AMS que el MIDlet no quiere estar “Activo” y que ha entrado en el estado de “Pausa”. Este método sólo debe ser invocado cuándo el MIDlet esté en el estado “Activo”. Una vez invocado este método, el MIDlet puede volver al estado “Activo” llamando al método MIDlet.startApp(), o ser destruido llamando al método MIDlet.destroyApp(). Si la aplicación es pausada por sí misma, es necesario llamar al método MIDlet.resumeRequest() para volver al estado “Activo”.

• protected abstract void pauseApp() Indica al MIDlet que entre en el estado de “Pausa”. Este método sólo debe ser llamado cuando el MIDlet esté en estado “Activo”. Si ocurre una excepción RuntimeException durante la llamada a MIDlet.pauseApp(), el MIDlet será destruido inmediatamente. Se llamará a su método MIDlet.destroyApp() para liberar los recursos ocupados.

• public final boolean platformRequest(String url)

Establece una conexión entre el MIDlet y la dirección URL. Dependiendo del contenido de la URL, nuestro dispositivo ejecutará una determinada aplicación que sea capaz de leer el contenido y dejar al usuario que interactúe con él. Si, por ejemplo, la URL hace referencia a un archivo JAD o JAR, nuestro dispositivo entenderá que se desea instalar la aplicación asociada a este archivo y comenzará el proceso de descarga. Si, por el contrario, la URL tiene el formato tel:<número>, nuestro dispositivo entenderá que se desea realizar una llamada telefónica.

• public final void resumeRequest() Este método proporciona un mecanismo a los MIDlets mediante el cual pueden indicar al AMS su interés en pasar al estado de “Activo”. El AMS, en consecuencia, es el encargado de determinar qué aplicaciones han de pasar a este estado llamando al método MIDlet.startApp().

• protected abstract void startApp() throws MIDletstateChangeException Este método indica al MIDlet que ha entrado en el estado “Activo”. Sólo puede ser invocado cuándo el MIDlet está en el estado de “Pausa”. En el caso de que el MIDlet no pueda pasar al estado “Activo” en este momento pero si pueda hacerlo en un momento posterior, se lanzaría la excepción MIDletstateChangeException.

A través de los métodos anteriores se establece una comunicación entre el AMS y el MIDlet.

Por un lado tenemos que los métodos startApp(), pauseApp() y destroyApp() los utiliza el AMS para comunicarse con el MIDlet, mientras que los métodos resumeRequest(), notifyPaused() y notifyDestroyed() los utiliza el MIDlet para comunicarse con el AMS. 3.4.2.2. Clase MIDletstateChangeException public class MIDletstateChangeException extends Exception

Esta excepción es lanzada cuando ocurre un fallo en el cambio de estado de un MIDlet. 3.4.3. ESTRUCTURA DE UN MIDLET

Los MIDlets al igual que los applets, carecen de la función main(). Aunque existiese, el gestor de aplicaciones la ignoraría por completo. Un MIDlet tampoco puede realizar una

J2ME

JXTA sobre J2ME - 40 -

llamada a System.exit(). Una llamada a este método lanzaría la excepción SecurityException. Los MIDlets tienen la siguiente estructura:

Estos métodos son los que obligatoriamente tienen que poseer todos los MIDlets ya que,

como hemos visto, la clase que hemos creado tiene que heredar de la clase MIDlet y ésta posee tres métodos abstractos: startApp(), pauseApp() y destroyApp() que han de ser implementados por cualquier MIDlet. 3.4.4 DESCARGAS VÍA OTA (OVER THE AIR)

Como vimos antes, el AMS debe procurar localizar el MIDlet deseado y una vez encontrado, proceder a su descarga al dispositivo. En el presente proyecto, la descarga de la aplicación se realiza vía OTA.

El término over-the-air-provisioning (OTA) describe la capacidad para descargar e instalar

contenido en una red inalámbrica. Desde la perspectiva del cliente de móvil, OTA es simplemente una forma de encontrar aplicaciones interesantes e iniciar su descarga a través de una red inalámbrica, tal como indica la Figura 3.8:

J2ME

JXTA sobre J2ME - 41 -

Figura 3.8: Descarga vía OTA

En este proceso participan:

1. Un dispositivo cliente: el cliente debe disponer de un sotfware que permita al usuario

localizar aplicaciones y permita elegir cuáles de ellas descargarse. Este software es conocido como discovery aplication (DA) y puede ser tanto un navegador como una aplicación nativa. Basta con que comparta un protocolo de transporte con el servidor de descargas. Para MIDP OTA este protocolo es HTTP.

2. La red: una red inalámbrica, que podría incluir una red vía radio y un gateway WAP. 3. El servidor de descargas: se trata de un host, visible en la red, donde normalmente

corre un servidor web y que tiene acceso al contenido almacenado. Tiene dos principales funciones: proporcionar menús en el formato apropiado, escritos normalmente en WML o HTML, que enumeran las aplicaciones disponibles para descargar, y proporcionar acceso a las aplicaciones en sí.

4. El contenido a descargar: se trata de los descriptores y las aplicaciones que están disponibles para descargar.

Los actuales sistemas OTA suelen incluir más elementos tales como control de los contenidos publicados, control de accesos, instalación y actualización de aplicaciones, y sistemas de facturación de lo descargado.

3.4.4.1. OTA para MIDP

La primera versión de la especificación MIDP OTA apareció tras la especificación MIDP 1.0. No es una especificación verdadera sino una recomendación práctica aunque proporciona una base para los protocolos OTA. Muchos dispositivos soportan actualmente la recomendación MIDP 1.0.

MIDP 2.0 mejoró la MIDP 1.0 y la hizo parte de la especificación. Debido a que ya forma

parte del estándar, todos los dispositivos MIDP futuros soportarán OTA de una forma consistente.

J2ME

JXTA sobre J2ME - 42 -

Las dos versiones son muy similares salvo pequeñas diferencias, como una nueva propiedad JAD usada por el AMS para informar cuando los MIDlets son borrados y comprobaciones más restrictivas al actualizar los MIDlets. También se ha sustituido el uso de cookies por reescritura de URL.

Los dispositivos MIDP deben proporcionar para soportar OTA: • Soporte para la versión 1.1 de HTTP o algún protocolo que implemente sus

funcionalidades. • Soporte para una aplicación que permita al usuario localizar aplicaciones en la red y

elegir cuál descargarse. • Soporte para el AMS, el software que controla los estados de la aplicación (descarga,

instalación, ejecución y eliminación). En los dispositivos que utilizan la J2ME también se conoce como Java application manager (JAM).

Como hemos comentado anteriormente, el AMS envía al servidor vía HTTP POST, un

informe de cualquier suceso ocurrido en cualquiera de las acciones llevadas a cabo. En la tabla 3.7 se encuentran los códigos de los distintos eventos posibles y si se encuentran en la MIDP 1.0 y 2.0: MIDP 1.0 2.0 900 Success x X

901 Insufficient Memory x X

902 User Cancelled x X

903 Loss of Service x X

904 JAR size mismatch x X

905 Attribute Mismatch x X

906 Invalid Descriptor x X

907 Invalid JAR X

908 Incompatible Configuration or Profile X

909 Application authentication failure X

910 Application authorization failure X

911 Push registration failure X

912 Deletion Notification X

913 Required package not supported by the device X

Tabla 3.7: Códigos de los posibles eventos

En relación a esto, se le pueden añadir al descriptor los siguientes tres campos, que sin ser de uso obligatorio, pueden resultar útiles:

• MIDlet-Install-Notify: localización URL donde se envían los informes sobre instalación y actualización.

• MIDlet-Delete-Notify (a partir de la MIDP 2.0): localización URL donde se envían los informes referentes a la eliminación de las aplicaciones.

J2ME

JXTA sobre J2ME - 43 -

• MIDlet-Delete-Confirm: el AMS usa este mensaje en el momento en que se quiere eliminar el MIDlet.

3.4.4.2. El lado del servidor en OTA

Para una mínima instalación OTA, es necesario disponer de un servidor web debidamente configurado para descargarse contenido, en este caso los archivos JAD y JAR de cada aplicación. Configurar el servidor consiste en definir los tipos MIME para los ficheros JAD y JAR: JAD text/vnd.sun.j2me.app-descriptor JAR application/java-archive

El servidor de descargas establece la cabecera Content-Type a estos valores y el software del cliente (AMS) usa esta cabecera tanto en la búsqueda de aplicaciones como en la instalación.

Dependiendo del servidor web que utilicemos, la configuración puede variar. Para el servidor Apache Tomcat, debemos añadir las siguientes líneas en el fichero de configuración que se encuentra en $CATALINA_HOME/conf/web.xml: <!-- JAD file --> <mime-mapping> <extension>jad</extension> <mime-type>text/vnd.sun.j2me.app-descriptor</mime-type> </mime-mapping> <!--JAR file --> <mime-mapping> <extension>jar</extension> <mime-type>application/java-archive</mime-type> </mime-mapping>

Tabla 3.8: Extracto del archivo web.xml 3.5. INTERFACES GRÁFICAS DE USUARIO

El perfil MIDP es el encargado de definir peculiaridades de un tipo de dispositivos, en nuestro caso, teléfonos móviles. Este perfil se ocupa de concretar aspectos como interfaces de usuario, sonidos, almacenamiento persistente, etc. En este apartado, vamos a explicar brevemente el paquete javax.microedition.lcdui (Interfaz de usuario con pantalla LCD), que es donde se encuentran los elementos gráficos que se utilizan en las aplicaciones. En el siguiente aparatado comentaremos el uso del almacenamiento persistente.

Realizaremos una división entre interfaces de usuario de alto nivel e interfaces de usuario de bajo nivel. En la interfaz de alto nivel, se usan componentes tales como botones, cajas de texto, formularios, etc. Al usar estos elementos, perdemos el control del aspecto de nuestra aplicación

J2ME

JXTA sobre J2ME - 44 -

ya que la estética de estos componentes depende exclusivamente del dispositivo donde se ejecute. En cambio, ganaremos un alto grado de portabilidad de la misma aplicación entre distintos dispositivos. Por otro lado tenemos la interfaz de bajo nivel, con la que tendremos un control total de lo que aparecerá por pantalla, pudiendo reconocer eventos de bajo nivel como, por ejemplo, el rastreo de pulsaciones de teclas. Generalmente, estas APIs se utilizan para la creación de juegos donde el control sobre lo que aparece por pantalla y las acciones del usuario juegan un papel fundamental.

En la figura 3.9 podemos ver las clases que componen el paquete javax.microedition.lcdui.

La interfaz de alto nivel deriva de la clase Screen mientras que la de bajo nivel lo hace de la clase Canvas:

Figura 3.9: Jerarquía de clases derivadas de Display e Item

En primer lugar vamos a ver una serie de clases comunes a ambas interfaces. Tanto para

éstas como para las que enumeraremos en los siguientes apartados, realizaremos sólo una breve descripción, remitiendo al lector interesado en su funcionamiento y en los métodos que las componen, a la bibliografía sobre J2ME que se adjunta.

• public class Display

La clase Display representa el manejador de la pantalla y los dispositivos de entrada. Todo MIDlet debe poseer por lo menos un objeto Display. La clase Display puede obtener información sobre las características de la pantalla del dispositivo donde se ejecute el MIDlet, además de ser capaz de mostrar los objetos que componen nuestras interfaces.

• public abstract class Displayable La clase Displayable representa a las pantallas de nuestra aplicación. Cada objeto Display puede tener tantos objetos Displayable como queramos. Las aplicaciones estarán formadas por varias pantallas que crearemos dentro del método constructor.

J2ME

JXTA sobre J2ME - 45 -

• public class Command Un objeto de la clase Command mantiene información sobre un evento. Podemos pensar en él como un botón de Windows, por establecer una analogía. Generalmente, los usaremos en nuestros MIDlets cuando queramos detectar y ejecutar una acción simple. Para ello tenemos que utilizar la interfaz CommandListener e implementar su método commandAction(Command c, Displayable d) en donde indicaremos la acción que queremos que se realice cuando se produzca un evento en el Command c que se encuentra en el objeto Displayable d.

3.5.1. INTERFAZ GRÁFICA DE USUARIO DE ALTO NIVEL

La clase Screen es la superclase de todas las clases que conforman la interfaz de usuario de alto nivel:

• public abstract class Screen extends Displayable Al ser abstracta no tiene definida ningún método. Vamos a ver las clases que derivan de ella: • public class Alert extends Screen

El objeto Alert representa una pantalla de aviso, como, por ejemplo, un error. Un Alert está formado por un título, texto e imágenes si queremos. Además podemos definir el tiempo que queremos que el aviso permanezca en pantalla.

• public class List extends Screen implements Choice La clase List nos va a permitir construir pantallas que poseen una lista de opciones, pudiendo crear menús. Implementa la interfaz Choice, dándonos la posibilidad de crear tres tipos distintos de listas cuyo tipo están definidos en esta interfaz. Lo vemos en la Tabla 3.9:

Tabla 3.9: Tipos de Listas

• public class TextBox extends Screen Una TextBox es una pantalla que nos permite editar texto en ella. Al crearla, tenemos que especificar el número de caracteres que queremos que albergue cómo máximo. Esta capacidad puede ser mayor que la que el dispositivo puede mostrar a la vez. En este caso, la implementación proporciona un mecanismo de scroll que permite visualizar todo el texto. Podemos también poner restricciones al texto que se puede incluir en una TextBox. Estas restricciones se encuentran en la clase TextField, que veremos más adelante.

• public class Form extends Screen Un formulario (clase Form) es un componente que actúa como contenedor de un número indeterminado de objetos. Todos los objetos que puede contener un formulario derivan de la clase Item. Un mismo Item no puede estar en más de un formulario a la vez. Si no cumplimos esta regla, se lanzaría la excepción IllegalStateException. El manejo de eventos de un formulario se hace de manera muy similar al de los Commands ya visto. Es necesario implementar la interfaz ItemStateListener que contiene un solo método abstracto itemStateChanged(Item item). Cuando realizamos algún tipo de acción en un

J2ME

JXTA sobre J2ME - 46 -

Item de un formulario, ejecutamos el código asociado a ese Item que definamos en dicho método. Los Item que podemos insertar en un formulario son: StringItem, ImageItem, TextField, DateField, ChoiceGroup y Gauge. Todos ellos los veremos a continuación.

• public class StringItem extends Item La clase StringItem es una cadena no modificable de texto, es decir, una cadena de texto con la que el usuario no puede interactuar de ninguna manera.

• public class ImageItem extends Item La clase ImageItem nos da la posibilidad de incluir imágenes en un formulario. Al igual que la clase StringItem, el usuario no podrá interactuar con la imagen.

• public class TextField extends Item Un TextField es un campo de texto que podemos insertar en un formulario y donde podemos editar texto. Ya hemos visto algo muy parecido cuándo estudiamos la clase TextBox. Las diferencias entre ambas son:

o Un TextField tiene que ser insertado en un formulario, mientras que un TextBox puede implementarse por sí mismo.

o TextField deriva de la clase Item, mientras que TextBox deriva directamente de Screen. Por lo tanto los eventos que produce un TextField los podemos controlar mediante el método itemStateChanged(Item item).

Ambas clases comparten las restricciones de entrada de las que hablamos, que quedan reflejadas en la Tabla 3.10:

Tabla 3.10: Restricciones de entrada de caracteres

• public class DateField extends Item El componente DateField nos permite manejar fechas y horas en nuestro formulario. Para ello, hace uso de la clase java.util.Date. El aspecto del objeto DateField depende del dispositivo MID dónde lo ejecutemos.

• public class ChoiceGroup extends Item implements Choice Un componente ChoiceGroup es un grupo de elementos que podemos seleccionar. Es prácticamente lo mismo que el componente List, pero dentro de un formulario. Podemos manejar los eventos de una ChoiceGroup a través del método itemStateChanged() o a través de commandAction().

• public class Gauge extends Item La clase Gauge implementa un indicador de progresos a través de un gráfico de barras. El componente Gauge representa un valor entero que va desde 0 hasta un valor máximo que definimos al crearlo. Un Gauge puede ser interactivo, en el que el usuario puede

J2ME

JXTA sobre J2ME - 47 -

modificar el valor actual del Gauge, y no interactivo, en el que el usuario no puede modificar su valor.

3.5.2. INTERFAZ GRÁFICA DE USUARIO DE BAJO NIVEL

La clase Canvas es la superclase de todas las pantallas que usan las APIs de bajo nivel, al igual que Screen lo era para las pantallas que usaban las APIs de alto nivel. No existe ningún impedimento que nos permita usar en el mismo MIDlet pantallas tanto derivadas de Canvas como de Screen.

La clase Canvas permite manejar eventos de bajo nivel y dibujar cualquier cosa por pantalla.

Por esta razón se usa como base para la realización de juegos. Esta clase posee un método abstracto paint() que debemos implementar obligatoriamente y es el que se encarga de dibujar en la pantalla del dispositivo MID.

3.5.2.1. Eventos de bajo nivel

Los eventos dentro de la clase Canvas podemos manejarlos principalmente de dos formas distintas:

• A través de Commands como hemos visto para la interfaz de alto nivel. • A través de códigos de teclas. Este método es el que Canvas proporciona para detectar

eventos de bajo nivel. Estos códigos son valores numéricos que están asociados a las diferentes teclas de un MID. De esta forma podemos conocer cual es la tecla que ha pulsado el usuario. Canvas, además proporciona unos métodos abstractos que permitirán manejar estos eventos, y que debemos implementar de acuerdo con el propósito para el que se ha creado la aplicación.

El perfil MIDP nos permite también detectar eventos producidos por dispositivos equipados

con algún tipo de puntero como un ratón o una pantalla táctil. 3.5.2.2. Método paint()

La clase Canvas posee un método abstracto paint(Graphics g) que se ocupa de dibujar el contenido de la pantalla. Para ello, se usa un objeto de la clase Graphics que es el que contiene las herramientas gráficas necesarias y que se le pasa como parámetro. Cuando vayamos a implementar este método tendremos que tener en cuenta lo siguiente:

• El método paint() nunca debe ser llamado desde el programa. El gestor de aplicaciones es el que se encarga de realizar la llamada a éste método cuando sea necesario.

• Cuando deseemos que se redibuje la pantalla actual debido a alguna acción en concreto del usuario o como parte de alguna animación, deberemos realizar una llamada al método repaint(). Al igual que ocurre con los eventos de teclado, la petición se encolará y será servida cuando retornen todas las peticiones anteriores a ella.

• La implementación del MID no se encarga de limpiar la pantalla antes de cada llamada al método paint(). Por esta razón, éste método debe pintar cada pixel de la pantalla para, de esta forma, evitar que se vean porciones no deseadas de pantallas anteriores.

J2ME

JXTA sobre J2ME - 48 -

La invocación al método paint() la realiza normalmente el gestor de aplicaciones cuando se pone la pantalla Canvas como pantalla activa a través del método de la clase Display setCurrent(Canvas) o después de invocar al método showNotify(). Hemos de tener en cuenta que una pantalla Canvas no posee la capacidad por sí misma de restaurar su estado en caso de que el AMS interrumpa la ejecución normal de la aplicación para, por ejemplo, avisar de una llamada entrante. Para resolver este inconveniente, Canvas nos proporciona dos métodos cuya implementación es vacía: hideNotify() y showNotify().

El primero de ellos es llamado justo antes de interrumpir a la aplicación y borrar la pantalla actual. En él podríamos incluir el código necesario para salvar el estado actual de la pantalla, variables, etc, y así posteriormente poder restaurar correctamente la aplicación. El método showNotify() es invocado por el gestor de aplicaciones justo antes de devolver éste el control a la aplicación. En él podemos insertar el código necesario para restaurar correctamente la pantalla de la aplicación. 3.5.2.3. La clase Graphics

• public class Graphics

Esta clase nos proporciona la capacidad de dibujar en una pantalla Canvas. Un objeto Graphics lo podemos obtener sólo de dos maneras:

o Dentro del método paint() de la clase Canvas. Aquí podemos usar el objeto Graphics para pintar en la pantalla del dispositivo.

o A través de una imagen. La clase Graphics posee multitud de métodos que nos permitirán seleccionar colores, dibujar texto, figuras geométricas, etc.

3.5.2.4. Eventos de teclado para juegos

Uno de los primeros campos que ha hecho uso de la tecnología MIDP es el de los videojuegos. Desde hace unos años, la mayoría de los teléfonos móviles traen incorporado algún que otro juego, la mayoría de ellos bastante simples. Dado el ‘boom’ tecnológico que ha dado la telefonía móvil en estos últimos años, podemos encontrar actualmente en el mercado teléfonos con pantalla a color de tamaño considerable, teléfonos que soportan GPRS, etc., con lo que los juegos han podido alcanzar un mayor desarrollo y complejidad.

Para favorecer esto, MIDP nos proporciona además de los códigos genéricos de teclado, un conjunto de constantes que se refieren a acciones específicas de un juego, tales como cursores de movimiento y botón de disparo. Dependiendo del dispositivo, cada uno de los códigos anteriores puede estar asignado a una tecla diferente. En cualquier caso, el programador no tiene por qué conocer a que tecla específica está asociada una acción en concreto, lo que facilita bastante el trabajo.

También se proporciona un paquete específico para juegos: javax.microedition.lcdui.Game.

Este paquete contiene un determinado número de clases que nos van a proporcionar una gran ayuda a la hora de crear juegos. Dado que estamos programando aplicaciones orientadas a dispositivos con ciertas restricciones computacionales, estas clases básicamente lo que

J2ME

JXTA sobre J2ME - 49 -

pretenden es aumentar el rendimiento de nuestra aplicación, minimizando en lo posible la carga de trabajo del programador y así, reducir el tamaño final de la aplicación. Estas clases que vamos a ver a continuación pueden usarse conjuntamente con las clases gráficas de bajo nivel. Este paquete se compone de cinco clases:

• Clase GameCanvas La clase GameCanvas es una clase abstracta que hereda de la clase Canvas y representa una pantalla básica para desarrollar un juego. Esta clase proporciona características especiales para juegos que nos permitirán por ejemplo, preguntar por el estado actual de las teclas del juego, además de sincronizar la aparición de gráficos por pantalla.

• Clase Layer La clase Layer (capa) representa un elemento visual en un juego y proporciona atributos tales como localización, tamaño y visibilidad. Este elemento puede ser un objeto Sprite o TiledLayer. Cualquier clase que creemos que herede de Layer debe implementar obligatoriamente el método paint(Graphics).

• Clase LayerManager Esta clase nos facilitará el trabajar con varios objetos de la clase Layer automatizando el proceso de dibujo de cada una de las capas. Además, permitirá crear vistas que representen la vista del usuario en los juegos.

• Clase Sprite La clase Sprite representa a un Layer animado. La clase Sprite contiene métodos que nos permitirán realizar transformaciones cómo rotaciones y detección de colisiones (evitar que se ocupe la misma posición en pantalla).

• Clase TiledLayer. Esta clase permitirá crear grandes áreas de contenido gráfico sin el problema que nos podría ocasionar tener una imagen de grandes dimensiones en memoria. Esto es posible debido a que un objeto TiledLayer está compuesto por una malla de celdas, las cuales pueden ser rellenadas con partes de una imagen.

3.5.2.5. Otras técnicas

Para terminar este apartado, veremos un par de técnicas que nos permitirán optimizar el código de un juego:

• Double Buffering: la técnica de pantalla en segundo plano evita que dispositivos que no poseen una gran capacidad computacional provoquen un parpadeo a la hora de actualizar los gráficos en pantalla. Para ello, se hacen todas las actualizaciones gráficas en memoria y posteriormente se vuelca el contenido de memoria en pantalla. Esta técnica es la que se conoce como double buffering. Algunos de estos dispositivos implementan esta técnica por su cuenta.

• Clipping: incluso implementando el double buffering no aseguramos que la actualización de la pantalla se produzca correctamente ya que algunos dispositivos no son suficientemente rápidos en las operaciones de lectura de memoria. Sería entonces interesante volver a dibujar sólo la parte de la pantalla que haya que modificarse dejando el resto en el mismo estado. Con la técnica de clipping definimos una región que limite lo que se va a pintar de nuevo en la llamada al método paint().

J2ME

JXTA sobre J2ME - 50 -

3.6. RECORD MANAGEMENT SYSTEM

Para el caso en que necesitemos guardar datos de forma persistente, que perduren más allá de la ejecución del MIDlet, disponemos de un mecanismo que está implementado sobre una base de datos basada en registros, que llamaremos Record Management System o RMS (Sistema de gestión de registros). Las clases para manipular el RMS se encuentran en el paquete javax.microedition.rms.

El sistema de gestión de registros o RMS nos permite almacenar información entre cada ejecución de nuestro MIDlet. Esta información será guardada en el dispositivo en una zona de memoria dedicada para este propósito. La cantidad de memoria y la zona asignada para ello dependerán de cada dispositivo.

Como ya se ha dicho, el RMS está implementado en una base de datos basada en registros. Los MIDlets son los encargados de crear los Record Stores para comunicarse con ellos. Estos Record Stores quedan almacenados en el dispositivo y pueden ser accedidos por cualquier MIDlet que pertenezca a la misma suite (ver Figura 3.10).

Figura 3.10: Comunicación entre MIDlets y RMS

3.6.1. RECORD STORES

Las propiedades de estos almacenes de registros son: 1. Cada Record Store está compuesto por cero o más registros. 2. Un nombre de Record Store es sensible a mayúsculas y minúsculas y está formado por un máximo de 32 caracteres UNICODE. 3. Dentro de una suite (conjunto de MIDlets) no pueden coexistir dos Record Stores con el mismo nombre. 4. Si una suite de MIDlets es borrada del dispositivo MID, todos los Record Stores

pertenecientes a esa suite se borrarán. 5. Es posible que un MIDlet acceda a un Record Store creado por otra suite, siempre que ésta de permiso para ello.

Un Record Store tal como su nombre indica es un almacén de registros. Estos registros son

la unidad básica de información que utiliza la clase RecordStore para almacenar datos. Cada

J2ME

JXTA sobre J2ME - 51 -

uno de estos registros está formado, entre otros elementos, por un número identificador de registro (Record ID) que realiza la función de clave primaria en la base de datos y un array de bytes que es utilizado para almacenar la información deseada.

La clase javax.microedition.rms.RecordStore nos proporciona una serie de métodos que nos permiten un conjunto de operaciones tales como abrir un RecordStore (nos lo crea en el caso de que no existiera), cerrarlo, borrarlo, ver el número de RecordStores que existen en la MIDlet suite, etc. 3.6.2. REGISTROS

Una vez abierta o creada la comunicación con un RecordStore, podemos leer, escribir, borrar o modificar registros. Para ello existen una serie de métodos de nuevo en la clase RecordStore.

Se pueden guardar un número variable de datos en cada registro utilizando streams de bytes

para introducirlos y para obtenerlos, pudiéndose ser los datos de muy distinto tipo. Para un manejo más eficiente de los registros, la clase RecordStore posee la interfaz

RecordEnumeration que pone a nuestra disposición un conjunto de métodos que, entre otras cosas, nos permitirá movernos a lo largo de los registros que formen el RecordStore.

Para realizar opciones de filtrado, contamos con la interfaz RecordFilter, que devuelve al

RecordEnumeration únicamente los registros que coincidan con un determinado patrón de búsqueda.

Con la interfaz RecordComparator podemos realizar una ordenación de los registros,

funcionando de manera similar a RecordFilter. Por ultimo, disponemos de una interfaz RecordListener que nos permite capturar eventos a

la hora de realizar cualquier acción en un RecordStore. Funciona igual que otros listeners, por lo que tenemos que implementar los métodos que determinen las acciones a realizar tras cada evento sucedido. 3.7. CONECTIVIDAD DE UN DISPOSITIVO MID

La gran ventaja que poseen los dispositivos MID es su capacidad de comunicación en cualquier momento y lugar. En este apartado vamos a ver que herramientas nos proporciona la plataforma J2ME para esa transferencia de información.

Para ello disponemos del paquete javax.microedition.io, el cual contiene las clases que nos dan soporte para el trabajo en red y comunicaciones en las aplicaciones MIDP. Este paquete contiene numerosas clases que nos permitirán crear y manejar diferentes conexiones de red: HTTP, datagramas, sockets,… También contaremos con el paquete java.io que se encargará de proporcionarnos las clases necesarias para leer y escribir en estas conexiones.

J2ME

JXTA sobre J2ME - 52 -

Estas clases orientadas a la conexión en red y comunicaciones reciben el nombre de Generic

Connection Framwork. En la Figura 3.11 podemos ver cómo se organizan jerárquicamente. También vamos a ver cómo se organizan las interfaces que nos suministra la especificación MIDP y que están en un nivel superior al GCF ya que implementan los detalles de diversos protocolos de comunicación.

Figura 3.11: Jerarquía de interfaces

Como vemos, la raíz del árbol es la interfaz Connection que representa la conexión más genérica y abstracta que podemos crear. El resto de interfaces que derivan de Connection representan los distintos tipos de conexiones que podemos crear. 3.7.1. CLASES Y CONEXIONES DEL GENERIC CONNECTION FRAMEWORK

Los dispositivos MID, como ya sabemos, poseen bastantes restricciones. Por esta razón, se hace imposible poseer la implementación de los distintos protocolos existentes. Lo que nos proporciona el Generic Connection Framework es una sola clase Connector que nos esconde los detalles de la conexión. Esta clase puede por sí misma crear cualquier tipo de conexión: Archivos, HTTP, socket, etc.

• public class Connector Nos esconde los detalles de la conexión, por lo que podemos realizar cualquier tipo de conexión usando sólo esta clase y sin preocuparnos de cómo se implementa el protocolo requerido. La clase Connector se encarga de buscar la clase específica que implemente el

J2ME

JXTA sobre J2ME - 53 -

protocolo requerido. Si esta clase se encuentra, el método open() devuelve un objeto que implementa la interfaz Connection.

• public abstract interface Connection La interfaz Connection se encuentra en lo más alto de la jerarquía de interfaces del Generic Connection Framework, por lo que cualquier otra interfaz deriva de él y representa a la conexión más abstracta y genérica posible.

• public abstract interface InputConnection extends Connection La interfaz InputConnection representa una conexión basada en streams de entrada.

• public abstract interface OutputConnection extends Connection La interfaz OutputConnection representa una conexión basada en streams de salida.

• public abstract interface StreamConnection extends InputConnection, OutputConnection Esta interfaz representa una conexión basada en streams tanto de entrada como de salida. Su única misión en la jerarquía del GCF es representar un tipo de conexión cuyos datos pueden ser tratados como streams de bytes y en la que es posible leer y escribir.

• public abstract interface ContentConnection extends StreamConnection Esta interfaz representa conexiones que pueden describir su contenido de alguna forma. En las conexiones anteriores transmitíamos bytes sin importarnos su composición, pero en estas conexiones la estructura de bytes a transmitir debe ser conocida de antemano. Con esta interfaz podemos conocer de antemano la longitud de datos que recibimos, con lo que las operaciones de lectura de datos se pueden simplificar mucho haciendo uso de esta información.

• public abstract interface StreamConnectionNotifier extends Connection Representa al establecimiento de conexiones lanzadas por clientes remotos. Si la conexión se realiza con éxito, se devuelve un StreamConnection para establecer la comunicación.

• public abstract interface DatagramConnection extends Connection Esta interfaz define las capacidades que debe tener una conexión basada en datagramas. A partir de esta interfaz se pueden definir distintos protocolos basados en datagramas

3.7.2. COMUNICACIONES HTTP

Ya hemos estudiado las clases e interfaces que conforman el Generic Connection

Framework. Estas interfaces nos dan un mecanismo por el cual podemos establecer un gran número de conexiones de distinto tipo. Como hemos dicho, el GCF nos esconde los detalles de la conexión y además no implementa ningún protocolo en concreto ya que esta implementación debe estar en el nivel de los perfiles. A continuación vamos a ver las interfaces implementadas por el perfil MIDP 2.0, empezando por la interfaz HttpConnection que implementa el protocolo HTTP 1.1.

El protocolo HTTP es un protocolo de tipo petición/respuesta: el cliente realiza una petición al servidor y espera a que éste le envíe una respuesta. Normalmente, esta comunicación es la que suele realizarse entre un navegador web (cliente) y un servidor web (servidor). Una conexión HTTP pasa por tres estados, como ilustra la Figura 3.12.

J2ME

JXTA sobre J2ME - 54 -

En el estado de establecimiento es dónde vamos a configurar los parámetros de la comunicación. El cliente prepara la petición que va a realizar al servidor, además de negociar con él una serie de parámetros como el tipo (GET, POST o HEAD), formato, idioma, etc. .

Figura 3.12: Estados de una conexión HTTP

En el estado de conexión se realiza el intercambio de información entre el cliente y el

servidor. La respuesta del servidor contendrá una serie de campos como una cabecera, línea de estado y un cuerpo de la respuesta, pudiendo informar el servidor desde que la conexión se ha producido con éxito, que ha habido algún error o mandar información al cliente.

Se entra en el estado de cierre una vez que se termina la comunicación entre el cliente y el

servidor. 3.7.3. OTRAS CONEXIONES

El perfil MIDP implementa varias interfaces de comunicación además del HTTP. Pasaremos a continuación a comentarlas brevemente:

• public interface HttpsConnection extends HttpConnection HTTPS es la versión segura del protocolo HTTP. Esta interfaz define los métodos necesarios para establecer una conexión de este tipo. En esta conexión, los parámetros de la petición se deben establecer antes de que ésta se envíe.

• public interface UDPDatagramConnection extends DatagramConnection Esta interfaz representa una conexión basada en datagramas en los que se conoce su dirección final que tiene que tener el siguiente formato: url = “datagram://<host>:<port>”. Si la cadena de conexión omite el host y el port, el sistema deberá de localizar un puerto libre.

• public interface CommConnection extends StreamConnection Esta interfaz representa una conexión mediante un puerto serie. En esta conexión los bits

de datos se transmiten secuencialmente. El formato de la dirección es el siguiente: url = “comm:<port><parámetros>”. Los parámetros van separados por “;”.

• public interface SocketConnection extends StreamConnection Esta interfaz define una conexión entre sockets basados en streams. La conexión con el socket de destino tiene el siguiente formato: url = “socket://<host>:<port>”.

J2ME

JXTA sobre J2ME - 55 -

• public interface SecureConnection extends SocketConnection Esta interfaz representa una conexión segura entre sockets. Esta conexión tiene el formato siguiente: url = “ssl://<host>:<port>”.

• public interface ServerSocketConnection extends StreamConnectionNotifier Esta interfaz representa una conexión con un servidor de sockets. Este tipo de conexión se establece cuando no especificamos el host.

JXME

JXTA sobre J2ME - 56 -

CAPÍTULO 4º: JXME 4.1. INTRODUCCIÓN

Hasta ahora hemos visto qué son y qué utilidad tienen los protocolos JXTA y, por otro lado, qué es la plataforma J2ME así como sus características principales. De aquí surge la pregunta de por qué no unir ambas tecnologías con el propósito de llevar las propiedades de la red P2P JXTA a los dispositivos móviles, contando con sus limitaciones.

El objetivo del proyecto JXTA para J2ME (JXME) es proporcionar funcionalidades

compatibles con JXTA a dispositivos de pequeña capacidad (móviles, PDAs,etc), usando la configuración CLDC y el perfil MIDP. Gracias a esto, cualquier dispositivo MID es capaz de participar tanto en actividades P2P con otros dispositivos MID como, con ciertas restricciones, en actividades P2P con nodos JXTA ejecutándose en PCs, servidores, etc.

JXME debe permitir a los terminales móviles:

1. El descubrimiento de nodos, grupos y tuberías. 2. La creación de nuevos nodos, grupos y aplicaciones. 3. Unirse a grupos. 4. Comunicarse con otros usuarios JXTA a través de tuberías JXTA.

Por el contrario, tiene como limitaciones:

1. El tamaño del MIDlet no debe ser mayor de 50k. 2. El almacenamiento permanente deber ser menor de 8k y compartido por todos los

MIDlets. 3. La pila de programas en ejecución es del orden de 32-64k. 4. El ancho de banda es limitado y la latencia es alta. 5. La potencia de la CPU es muy limitada (sobre 20 MHz). 6. La vida de la batería es muy limitada.

Existen dos versiones del proyecto JXME actualmente: la versión con proxy, en la que

existe una figura intermedia llamada proxy o relay, que acapara ciertas funciones que el dispositivo móvil no es capaz de realizar, y que es sobre la que se ha realizado la aplicación, y otra versión sin proxy, en la que se busca que el móvil posea todas las funcionalidades propias de un nodo JXTA y que está aún en fase de estudio. 4.2. VERSIÓN CON PROXY

Para salvar las limitaciones de conectividad propias de los terminales móviles que vienen recogidas en el perfil MIDP 1.0, aparece la figura del proxy o relay, un nodo rendezvouz JXTA que cuenta con las siguientes capacidades:

JXME

JXTA sobre J2ME - 57 -

• Filtra los advertisements (anuncios de otros nodos, tuberías, etc.): los nodos JXME no tienen suficiente capacidad de almacenamiento para todos los advertisements que le llegan, luego el relay filtra los innecesarios y compacta el resto a lo mínimo permitido por el dispositivo JXME.

• Traduce los mensajes JXTA en formato XML a mensajes binarios entendibles por los nodos JXME y viceversa.

• Actúa como proxy en nombre del nodo JXME. • Retransmite los mensajes, permitiendo a los usuarios JXTA estar detrás de NATs. Para

los nodos JXME además, almacenan los mensajes entrantes, por lo que el nodo sondea periódicamente al relay para obtenerlos.

Figura 4.1: JXME versión proxy

En resumen, el nodo JXME solo habla con el nodo relay, enviándole solicitudes HTTP y obteniendo las respuestas sondeándolo periódicamente. El relay es el nodo con toda la funcionalidad JXTA, incluido el tratamiento de los mensajes XML, utilizando JXME formato binario para simplificar. Por lo tanto un nodo JXME más un nodo relay completan la funcionalidad de un nodo JXTA completo:

Figura 4.2: Funcionalidad nodo J2ME más relay

JXME

JXTA sobre J2ME - 58 -

4.3. VERSIÓN SIN PROXY

Debido a la mejora de las prestaciones de la nueva generación de dispositivos móviles, se dan las circunstancias propicias para elaborar una mejora del proyecto JXME, eliminando la figura del nodo relay que hacía de intermediario entre el nodo JXME y la red JXTA. Para ello, las funcionalidades realizadas anteriormente por el relay se traspasan al propio dispositivo móvil. Estas modificaciones vienen recogidas en el perfil MIDP 2.0.

La versión sin proxy está en fase de estudio en la actualidad, detallándose en la página del proyecto JXME (http://jxme.jxta.org/) las novedades que van surgiendo con respecto a esta versión.

Todo lo visto antes para la versión con proxy se da también para esta versión, tanto capacidades como limitaciones, permitiendo la comunicación del dispositivo MIDP ya sea con otros dispositivos MIDP (1.0 como 2.0) como con otros nodos JXTA (siempre con ciertas restricciones).

Figura 4.3: Diferencias entre la versión con proxy y sin proxy

Para implementar JXME, no es necesario tener que implementar todos los protocolos JXTA. En principio, basta con utilizar los siguientes:

1. Peer Discovery Protocol (PDP): Usado por los nodos para anunciar los distintos

recursos (por ejemplo nodos, grupos de nodos, tuberías o servicios) y descubrir recursos de otros nodos. Cada recurso del nodo se describe y publica usando un advertisement.

2. Peer Resolver Protocol (PRP): Permite a los nodos el enviar una consulta genérica a uno o más nodos y recibir una respuesta (o respuestas múltiples) para la consulta.

3. Endpoint (Routing) Protocol (ERP): Usado por los nodos para encontrar rutas (caminos) para los puertos de destino en otros nodos.

JXME

JXTA sobre J2ME - 59 -

4. Pipe Binding Protocol (PBP): Usado por los nodos para establecer un canal de comunicación virtual, pipe o tubería, entre uno o más nodos. El PBP es usado por un nodo para ligar dos o más finales de la conexión (los puntos finales de tubería).

Debido a las limitaciones de los dispositivos utilizados, es recomendable implementar sólo los elementos requeridos de cada protocolo y eliminar el resto.

Aplicación Desarrollada

JXTA sobre J2ME - 60 -

CAPÍTULO 5º:

APLICACIÓN DESARROLLADA

5.1. ESTRUCTURA DE LA APLICACIÓN

A continuación se procederá a comentar las clases y métodos que componen la aplicación, con una breve explicación de su funcionalidad.

Aparte de las clases implementadas para la aplicación que se comentan en este apartado, se utilizan una serie de clases proporcionadas por JXTA para aquellas aplicaciones JXME que requieran de la utilización de un proxy/relay. Estas clases son:

• public final class PeerNetwork: se encarga de proporcionar algunas de las funcionalidades propias de un nodo JXTA (crear y cerrar tuberías, escuchar en ellas, envíar datos, búsqueda de recursos en la red) así como la propiedad de conectarse a un relay y de sondearlo para obtener los mensajes destinados a la aplicación.

• public final class Message: representa un mensaje JXTA que está compuesto por cierto número de elementos, algunos de ellos reservados para la red JXTA.

• public final class Element: esta clase representa un elemento de un mensaje JXTA. • final class ByteCounterOutputStream extends OutputStream: esta clase extiende a

OutputStream y tiene como funcionalidad el poder contar los bytes de un stream sin tener que almacenarlo en memoria. Se utiliza en la clase Message.

• final class HttpMessenger: proporciona al nodo JXME facilidades para el intercambio de mensajes JXTA estableciendo una conexión con el relay JXTA.

Estas clases se encuentran en el paquete net.jxta.j2me e incluiremos su código en un apartado posterior, junto con el de la aplicación.

Las clases que componen propiamente la aplicación son:

• public final class Michat extends MIDlet implements CommandListener: Se trata de la clase principal de la aplicación al ser la que extiende a la clase MIDlet. Por lo tanto, implementará los métodos propios de un MIDlet: startApp(), pauseApp() y destroyApp(). Por otra parte se encarga de crear la página principal que contiene el menú de opciones. Para efectuar la acción correspondiente a la opción elegida, la clase implementa CommandListener, que detecta el evento de pulsar una tecla. También actúa de intermediaria entre el resto de clases, contando para ello con métodos públicos que invocarán a su vez a métodos de las demás clases.

Aplicación Desarrollada

JXTA sobre J2ME - 61 -

• public class Operaciones implements CommandListener: Es la clase donde se realizan las distintas operaciones necesarias para la aplicación tales como conectarse, enviar mensajes, sondear el relay, etc. Implementa CommandListener para detectar las opciones elegidas en las distintas pantallas que contiene la clase.

• public class Buddy implements CommandListener: En esta clase se encuentran los métodos que permiten operaciones con los contactos tales como recuperarlos y guardarlos en el RMS y añadir y borrarlos a la lista de contactos. Implementa CommandListener para detectar los eventos asociados a la selección de opciones en la pantalla asociada a esta clase.

• public class Conf implements CommandListener: La clase Conf será la encargada de almacenar y recuperar la información sobre la configuración de la aplicación. Para ello utilizaremos el mecanismo de almacenamiento permanente (RMS) del que disponen los dispositivos MID. Implementa un CommandListener para detectar las opciones elegidas en el formulario que se crea en la clase.

• public class PollThread extends Thread: Se trata de la clase que implementa el hilo que se ejecutará en segundo plano para realizar la operación periódica de sondeo del relay. Dentro de la clase, también se efectuará la llamada al método connect de la clase Operaciones a través de la clase Michat y se enviará el mensaje de conexión al NetPeerGroup a través de la tubería del IP2P Group. Cuando trata de realizar la conexión, implementará una barra de progreso para lo que hará uso de las clases StatusUpdate, Timer, TimerTask y Gauge.

• public class StatusUpdate extends TimerTask: La clase StatusUpdate se encarga de crear una barra de progreso que irá incrementándose tras la solicitud de conexión y hasta que ésta se haya completado. Para ello, StatusUpdate extiende a la clase TimerTask y se utiliza para crear un objeto Timer en la clase PollThread, que producirá un evento cada segundo. Cada vez que se capture este evento, se ejecutará el método run de StatusUpdate.

5.1.1. CLASE MICHAT

public final class Michat extends MIDlet implements CommandListener

A continuación enumeramos los atributos de la clase Michat y procedemos a describir su función:

private static final int DEFAULT_POLL_INTERVAL = 1;

private static final int DEFAULT_SCROLL = 3;

private static final String RECORD_STORE_NAME = "jxta-midp-chat";

private static final boolean DEBUG = true;

private static final String INSTANTP2P_GROUPNAME = "IP2PGRP";

protected Display display = null;

private Form initForm = null;

private String currentBuddy = null;

private int pollInterval = DEFAULT_POLL_INTERVAL;

private PollThread pollThread = null;

protected boolean stopPolling = false;

private boolean sendPending = false;

Aplicación Desarrollada

JXTA sobre J2ME - 62 -

private Buddy buddy;

private Conf config;

private Operaciones op;

• DEFAULT_POLL_INTERVAL: constante que representa el intervalo de sondeo del relay por defecto en segundos.

• DEFAULT_SCROLL: constante que determina el máximo número de elementos (ítems) que vamos a permitir que aparezcan a la vez en los formularios. Cuando se supera se elimina de pantalla el primero.

• RECORD_STORE_NAME: constante que guarda el nombre del Record Store para el almacenamiento persistente (RMS).

• DEBUG: constante de control para imprimir en la salida estándar. • INSTANTP2P_GROUPNAME: constante con el nombre del grupo de chat de IP2P. • display: variable que representa la pantalla del MID. Se declara protected porque será

utilizada para crear todas las pantallas en el resto de clases. • initForm: formulario que representa la pantalla principal con el menú de opciones. • currentBuddy: variable que almacena el usuario con el que se va a hablar, ya sea uno

para abrirle un privado o el IP2P Group. • pollInterval: variable que guarda el intervalo de sondeo en segundos. • pollThread: objeto del hilo que se encargará del sondeo al relay. • stopPolling: variable que se encarga de determinar cuando deber estar ejecutándose o

parándose el hilo. Se declara protected ya que será utilizada por varias clases. • sendPending: variable que indica si hay mensajes pendientes de enviar. • buddy: objeto de la clase Buddy que será utilizado dentro de la clase Michat. Se le pasa

como parámetro el propio MIDlet. • config: objeto de la clase Conf que será utilizado dentro de la clase Michat. Se le pasa

como parámetro el propio MIDlet. • op: objeto de la clase Operaciones que será utilizado dentro de la clase Michat. Se le

pasa como parámetro el propio MIDlet.

A continuación nombramos los métodos de la clase Michat y a continuación hacemos una breve descripción de su funcionamiento:

public Michat() {

initForm = new Form("JXTA Chat");

buddy = new Buddy(this);

config = new Conf(this);

op = new Operaciones(this);

buddy.readBuddies();

config.readConfig();

op.setupConfirmForm();

Command cmdSend = new Command("Enviar", Command.SCREEN, 1);

Command cmdConnect = new Command("Conectar", Command.SCREEN, 2);

Command cmdBuddies = new Command("Lista de contactos",

Command.SCREEN, 3);

Command cmdConfig = new Command("Configuracion", Command.SCREEN, 4);

Command cmdDefault = new Command("Opciones por defecto",

Aplicación Desarrollada

JXTA sobre J2ME - 63 -

Command.SCREEN, 5);

Command cmdExit = new Command("Exit", Command.EXIT, 6);

initForm.addCommand(cmdSend);

initForm.addCommand(cmdConnect);

initForm.addCommand(cmdBuddies);

initForm.addCommand(cmdConfig);

initForm.addCommand(cmdDefault);

initForm.addCommand(cmdExit);

initForm.setCommandListener(this);

}

public void commandAction(Command c, Displayable d) {

if (c.getCommandType() == Command.EXIT) {

destroyApp(true);

notifyDestroyed();

return;

}

String label = c.getLabel();

if (d == initForm) {

if (label.equals("Enviar")) {

currentBuddy = INSTANTP2P_GROUPNAME;

verSendForm();

} else if (label.equals("Configuracion")) {

config.editConfig();

} else if (label.equals("Lista de contactos")) {

buddy.editBuddies();

} else if (label.equals("Conectar")) {

op.initiateConnect();

} else if (label.equals("Opciones por defecto")) {

op.verConfirmForm();

}

}

}

public void startApp() {

display = Display.getDisplay(this);

if ("".equals(config.obtenerIdentidad())) {

config.editConfig();

} else {

display.setCurrent(initForm);

}

stopPolling = false;

pollThread = new PollThread(this);

}

public void pauseApp() {

stopPolling = true;

pollThread = null;

}

public void destroyApp(boolean unconditional) {

try {

op.sendHello(" has left NetPeerGroup"); }

catch (Exception e) {}

stopPolling = true;

Aplicación Desarrollada

JXTA sobre J2ME - 64 -

pollThread = null;

config.storeConfig();

buddy.storeBuddies();

op.ponerPeerANull();

}

public void resetConfig() {

try {

saludar(" has left NetPeerGroup");}

catch(Exception e) {}

if (op.connected) {

op.disconnect();

}

op.ponerPeerANull();

byte[] st = new byte[0];

config.establecerState(st);

try {

RecordStore.deleteRecordStore(RECORD_STORE_NAME);

} catch (RecordStoreException ex) {

if (DEBUG) {

System.out.println(ex);

}

}

config.ponerConfigFormNull();

buddy.readBuddies();

config.readConfig();

currentBuddy = null;

config.editConfig();

}

public void verInitForm()

{

display.setCurrent(initForm);

}

public void verAlertInitForm(Alert alert)

{

display.setCurrent(alert,initForm);

}

public void cambiarCurrentBuddy(String cBuddy)

{

currentBuddy = cBuddy;

}

public String obtenerCurrentBuddy()

{

return currentBuddy;

}

public int obtenerPollInterval()

{

return pollInterval;

Aplicación Desarrollada

JXTA sobre J2ME - 65 -

}

public void establecerPollInterval(int interval)

{

pollInterval = interval;

}

public void verEstablecerSelectedPipe(String sp)

{

op.establecerSelectedPipe(sp);

}

public String obtenerIdent()

{

return config.obtenerIdentidad();

}

public String obtenerHost()

{

return config.obtenerDirHost();

}

public String obtenerPort()

{

return config.obtenerNumPort();

}

public String obtenerCurrentBuddyId()

{

return buddy.obtenerBuddyId(currentBuddy);

}

public boolean verCondConnect()

{

if (!op.connected && op.connectInitiated)

return true;

else return false;

}

public void dormirHilo()

{

try{

pollThread.sleep(pollInterval*1000);}

catch(Exception e){}

}

public boolean conectar() throws Exception

{

try {

op.connect();

return true;}

catch (Exception e) {

return false;}

}

public void saludar(String saludo)

{

Aplicación Desarrollada

JXTA sobre J2ME - 66 -

op.sendHello(saludo);

}

public void verBusqueda(String name)

{

op.busqueda(name);

}

public void establecerSendPending(boolean estado)

{

sendPending = estado;

}

public boolean obtenerSendPending()

{

return sendPending;

}

public boolean enviar()

{

return op.send();

}

public void verAñadirBuddy(String name, String id)

{

buddy.añadirBuddy(name,id);

}

public void cambiarInitForm(StringItem si)

{

if (initForm.size() >= DEFAULT_SCROLL) {

initForm.delete(0);

}

initForm.append(si);

}

public void verSendForm()

{

op.sendForm();

}

public byte[] verObtenerState ()

{

return config.obtenerState();

}

public void verEstablecerState(byte[] state)

{

config.establecerState(state);

}

public boolean sondear()

{

return op.poll();

}

Aplicación Desarrollada

JXTA sobre J2ME - 67 -

• Michat(): constructor de la clase, donde creamos el formulario con la página de inicio, añadiéndole las opciones del menú, inicializamos los objetos del resto de clases y leemos la configuración y la lista de contactos almacenados en memoria.

• commandAction(Command c, Displayable d): método donde se determinan las acciones a realizar cuando el CommandListener recoge un evento de pulsación de tecla, en este caso del formulario initForm.

• startApp(): método propio de todos los MIDlets donde se establecen las acciones a realizar cada vez que se empieza a ejecutar la aplicación. En este caso, entre otras cosas, se establece el manejador de pantallas y crea el hilo.

• pauseApp(): se establecen las acciones a realizar cada vez que se pausa la aplicación. En este caso se para la ejecución del hilo.

• destroyApp(boolean unconditional): se establecen las acciones a realizar cada vez que se destruye la aplicación. Aquí se manda el mensaje de salida del NetPeerGroup, se destruye el hilo, se almacena la configuración y la lista de contactos y nos desconectamos de la red JXTA.

• resetConfig(): mandamos el mensaje de salida del NetPeerGroup, nos desconectamos de la red JXTA y borramos el almacenamiento persistente, teniendo que volver a configurar la aplicación.

• verInitForm(): sirve para establecer como pantalla activa el formulario initForm desde cualquier otra clase.

• verAlertInitForm(Alert alert): desde cualquier otra clase, se muestra la alerta alert y posteriormente se muestra la pantalla initForm.

• cambiarCurrentBuddy(String cBuddy): se cambia el valor de la variable currentBuddy. • obtenerCurrentBuddy(): obtenemos el valor de la variable currentBuddy. • obtenerPollInterval(): conseguimos el valor de la variable pollInterval. • establecerPollInterval(int intervalo): cambiamos el valor de la variable pollInterval. • verEstablecerSelectedPipe(String sp): llamamos al método establecerSelectedPipe

(String sp) del objeto op para cambiar la tubería para privados seleccionada. • obtenerIdent(): llamamos al método obtenerIdentidad() del objeto config para obtener el

nick que se ha introducido. • obtenerHost(): llamamos al método obtenerDirHost() del objeto config para obtener la

dirección del relay que se ha introducido. • obtenerPort(): llamamos al método obtenerNumPort() del objeto config para obtener el

puerto del relay que se ha introducido. • obtenerCurrentBuddyId(): llamamos al método obtenerBuddyId(currentBuddy) del

objeto buddy para obtener el Id de la tubería del contacto pasado como parámetro. • verCondConnect(): comprueba si se da una condición en el objeto op y devuelve lo

obtenido. • dormirHilo(): duerme el hilo durante el tiempo en segundos establecido por pollInterval.

Captura la excepción si se produce. • conectar(): llama al método connect() del objeto op. Lanza una excepción en el caso de

que no se pueda realizar. • saludar(String saludo): llama al método sendHello(String name) del objeto op para

enviar a la tubería del IP2P Group la cadena saludo.

Aplicación Desarrollada

JXTA sobre J2ME - 68 -

• verBusqueda(String name): llama al método búsqueda(String name) del objeto op para comenzar una búsqueda de usuarios que tengan por nombre name. Si el parámetro es null se realiza una búsqueda general de usuarios de chat JXTA.

• establecerSendPending(boolean estado): cambia el valor de la variable sendPending. • obtenerSendPending(): devuelve el valor de la variable sendPending. • enviar(): llama al método send() del objeto op para enviar un mensaje a la tubería de

contacto especificado por currentBuddy. • verAñadirBuddy(String name, String id): llamamos al método añadirBuddy(String

name, String id) del objeto buddy para añadir a la lista de contactos el usuario con nombre name y tubería con identificador id.

• cambiarInitForm(StringItem si): añadimos al formulario initForm el StringItem si. Si el que añadimos hace que se supere el número de ítems especificado por DEFAULT_SCROLL, borramos el primero que haya en initForm.

• verSendForm(): llamamos al método sendForm() del objeto op que me crea el formulario donde introducimos el mensaje a enviar.

• verObtenerState (): llamamos al método obtenerState() del objeto config para conseguir el valor de la variable state que se utiliza para identificar conexiones por parte del mismo nodo.

• verEstablecerState(byte[] state): llamamos al método establecerState(byte[] state) del objeto config paera cambiar el valor de la variable state que se utiliza para identificar conexiones por parte del mismo nodo.

• sondear(): llamamos al método poll() del objeto op que nos permite sondar al relay. 5.1.2. CLASE OPERACIONES

public class Operaciones implements CommandListener Los atributos de dicha clase y su función son los siguientes: private static final int DEFAULT_ALERT_TIMEOUT = 5000;

private static final String INSTANTP2P_PIPEID = "urn:jxta:uuid-" +

"59616261646162614E50472050325033" +

"D1D1D1D1D1D141D191D191D1D1D1D1D104";

private static final boolean QUANTIFY = true;

private static final boolean DEBUG = true;

private static final String TALKNAME_PREFIX = "JxtaTalkUserName.";

private static final String INSTANTP2P_GROUPNAME = "IP2PGRP";

private Michat midlet;

private Form replyForm = null;

private Form confirmForm = null;

private TextField tfSentMsg = null;

private PeerNetwork peer = null;

private int responseId = -1;

private String selectedPipe = null;

protected boolean connected = false;

protected boolean connectInitiated = false;.

Aplicación Desarrollada

JXTA sobre J2ME - 69 -

• DEFAULT_ALERT_TIMEOUT: constante que especifica el tiempo que deben durar las alertas.

• INSTANTP2P_PIPEID: constante que guarda el identificador de la tubería del IP2P Group.

• QUANTIFY: constante de control para cuando se realizan llamadas a métodos del sistema para la obtención de fechas y horas.

• DEBUG: constante de control para imprimir en la salida estándar. • TALKNAME_PREFIX: prefijo que llevan todos lo usuarios de las aplicaciones de chat

JXTA. • INSTANTP2P_GROUPNAME: constante con el nombre del grupo de chat de IP2P. • midlet: objeto de la clase Michat que utilizaremos para usar sus métodos. • replyForm: formulario donde se crea el mensaje a enviar. • confirmForm: formulario para restablecer la configuración original. • tfSentMsg: campo de texto donde se introduce el mensaje en en replyForm. • peer: objeto de PeerNetwork que es la clase que proporciona JXTA para

implementaciones que necesiten la utilización de relays. • responseId: a cada consulta que se realiza a traves del relay se le asigna un número para

poder identificar la posible respuesta que vendrá con el mismo número. Este número de la respuesta se almacena en responseId.

• selectedPipe: variable que almacena el identificador de las tuberías que se utilizan para enviar privados.

• connected: variable que indica que estamos conectados al relay. • connectInitiated: indica que hemos solicitado la conexión. Se utiliza para que no se

realice nada más que una petición de conexión.

Los métodos que contiene la clase Operaciones son:

public Operaciones(Michat midlet)

{

this.midlet = midlet;

}

public void sendForm() {

if (replyForm == null) {

replyForm = new Form(midlet.obtenerCurrentBuddy());

tfSentMsg = new TextField(null,

null,

4096,

TextField.ANY);

replyForm.append(tfSentMsg);

Command cmdSend = new Command("Enviar", Command.SCREEN, 1);

Command cmdBack = new Command("Volver", Command.BACK, 2);

replyForm.addCommand(cmdSend);

replyForm.addCommand(cmdBack);

replyForm.setCommandListener(this) ;

}

replyForm.setTitle(midlet.obtenerCurrentBuddy());

midlet.display.setCurrent(replyForm);

Aplicación Desarrollada

JXTA sobre J2ME - 70 -

}

public void setupConfirmForm() {

confirmForm = new Form("Restablecer configuracion");

Command cmdOK = new Command("OK", Command.OK, 1);

Command cmdBack = new Command("Volver", Command.BACK, 2);

StringItem si = new StringItem(null,

"Restablecer configuracion y lista de

contactos por defecto, continuar?");

confirmForm.addCommand(cmdOK);

confirmForm.addCommand(cmdBack);

confirmForm.setCommandListener(this);

confirmForm.append(si);

}

public boolean send() {

if (peer == null || !connected) {

initiateConnect();

midlet.establecerSendPending(true);

return false;

}

String msg = tfSentMsg.getString();

Element[] elm = new Element[3];

elm[0] = new Element("JxtaTalkSenderName",

midlet.obtenerIdent().getBytes(),

null, null);

elm[1] = new Element("JxtaTalkSenderMessage",

msg.getBytes(),

null, null);

elm[2] = new Element("GrpName",

"NetPeerGroup".getBytes(),

null, null);

Message m = new Message(elm);

try {

String pipeId = midlet.obtenerCurrentBuddyId();

peer.send(pipeId, m);

} catch (Exception ex) {

Alert alert = new Alert("Enviar",

"Error enviando mensaje: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

return false;

}

return true;

}

public void sendHello(String saludo){

Element[] elm = new Element[3];

elm[0] = new Element("JxtaTalkSenderName",

midlet.obtenerIdent().getBytes(),

null, null);

elm[1] = new Element("JxtaTalkSenderMessage",

saludo.getBytes(),

null, null);

elm[2] = new Element("GrpName",

Aplicación Desarrollada

JXTA sobre J2ME - 71 -

"NetPeerGroup".getBytes(),

null, null);

Message m = new Message(elm);

try {

peer.send(INSTANTP2P_PIPEID, m);

midlet.dormirHilo();

}catch (Exception ex) {

Alert alert = new Alert("Enviar",

"Error enviando mensaje: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

}

public void initiateConnect() {

if (peer == null) {

peer = PeerNetwork.createInstance(midlet.obtenerIdent());

}

if (connected || connectInitiated) {

return;

}

connectInitiated = true;

// se continua la ejecucion en el hilo

}

public boolean connect() throws Exception {

connectInitiated = false;

String host = midlet.obtenerHost();

int port = 0;

try {

port = Integer.parseInt(midlet.obtenerPort());

} catch (NumberFormatException ex) {

Alert alert = new Alert("Conectar",

"Error obteniendo el puerto del relay: " +

midlet.obtenerPort(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

return false;

}

String url = "http://" + host + ":" + Integer.toString(port);

if (DEBUG) {

System.out.println("Conectandose a " + url + "...");

}

long startTime, endTime;

if (QUANTIFY) {

startTime = System.currentTimeMillis();

}

byte[] state = midlet.verObtenerState();

state = peer.connect(url, state);

midlet.verEstablecerState(state);

if (QUANTIFY) {

Aplicación Desarrollada

JXTA sobre J2ME - 72 -

endTime = System.currentTimeMillis();

System.out.println("conexion llevo " +

Long.toString(endTime-startTime));

}

connected = true;

String ip2pIdentity = TALKNAME_PREFIX + INSTANTP2P_GROUPNAME;

String miChatIdentity = TALKNAME_PREFIX + midlet.obtenerIdent();

if (DEBUG) {

System.out.println("creando tuberia ");

}

boolean ip2p = false;

boolean miPipe = false;

int idIP2PPipe = peer.create(PeerNetwork.PIPE, ip2pIdentity,

INSTANTP2P_PIPEID, "JxtaPropagate");

int idMiPipe = peer.create(PeerNetwork.PIPE, miChatIdentity,

selectedPipe, "JxtaUnicast");

System.out.println("ids de las tuberias "+idIP2PPipe+"\n"+idMiPipe);

while (!(ip2p&&miPipe))

{

poll();

System.out.println("esto es"+responseId);

if (responseId == idIP2PPipe)

ip2p = true;

if (responseId == idMiPipe)

miPipe = true;

if (midlet.stopPolling) {

System.out.println("salimos del bucle");

return false; }

try {

midlet.dormirHilo();

System.out.println("dormido");

} catch (Throwable t) {}

}

if (DEBUG) {

System.out.println("Escuchando en " + INSTANTP2P_PIPEID+"\n"+

selectedPipe);

}

peer.listen(INSTANTP2P_PIPEID);

peer.listen(selectedPipe);

midlet.verInitForm();

return true;

}

public void disconnect() {

//cierro la tuberia y me desconecto

try {

peer.close(INSTANTP2P_PIPEID);

peer.close(selectedPipe);

connected = false;

if (DEBUG) {

System.out.println("Cerrada la tuberia");

Aplicación Desarrollada

JXTA sobre J2ME - 73 -

}

} catch (Exception ex) {

Alert alert = new Alert("Desconectando",

"Error conectando con el relay: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(Alert.FOREVER);

midlet.verAlertInitForm(alert);

}

}

public boolean poll() {

if (peer == null || !connected) {

// no estoy conectado aun

return false;

}

Message msg = null;

try {

long startTime, endTime;

if (QUANTIFY) {

startTime = System.currentTimeMillis();

}

if (peer != null) {

msg = peer.poll(midlet.obtenerPollInterval()*1000);

}

if (QUANTIFY) {

endTime = System.currentTimeMillis();

System.out.println("sondeo llevo " +

Long.toString(endTime-startTime));

}

} catch (Exception ex) {

//ex.printStackTrace();

Alert alert = new Alert("Sondeo",

"Error sondeando el relay: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

return false;

}

if (msg == null) {

return false;

}

Element el = null;

String name = null;

String id = null;

for (int i=0; i < msg.getElementCount(); i++) {

el = msg.getElement(i);

if (Message.PROXY_NAME_SPACE.equals(el.getNameSpace())) {

String elementName = el.getName();

if (Message.NAME_TAG.equals(elementName)) {

name = new String(el.getData());

}else if (Message.ID_TAG.equals(elementName)) {

Aplicación Desarrollada

JXTA sobre J2ME - 74 -

id = new String(el.getData());

}

}

}

if (name != null && id != null && !"".equals(id)) {

if (name.indexOf(TALKNAME_PREFIX) >= 0) {

name = name.substring(TALKNAME_PREFIX.length());

}

midlet.verAñadirBuddy(name,id);

}

String sender = null;

String message = null;

for (int i=0; i < msg.getElementCount(); i++) {

el = msg.getElement(i);

if ("requestId".equals(el.getName())) {

responseId = Integer.parseInt(new String(el.getData()));

}else if ("JxtaTalkSenderName".equals(el.getName())) {

sender = new String(el.getData());

} else if ("JxtaTalkSenderMessage".equals(el.getName())) {

message = new String(el.getData());

}

if (DEBUG) {

System.out.print(i + " " +el.getNameSpace() +" " +

el.getName());

System.out.print(" " + new String(el.getData()));

System.out.println();

}

}

if (sender != null && message != null) {

if (sender.indexOf(TALKNAME_PREFIX) >= 0) {

sender = sender.substring(TALKNAME_PREFIX.length());

}

String displayedMsg = null;

if (id.equals(INSTANTP2P_PIPEID))

displayedMsg = sender + "@" + INSTANTP2P_GROUPNAME + "> " +

message + "\n";

else if(id.equals(selectedPipe))

displayedMsg = sender + ">" + message + "\n";

StringItem si = new StringItem(null, displayedMsg);

midlet.cambiarInitForm(si);

Alert alert = new Alert("","",

null, AlertType.INFO);

alert.setTimeout(1);

midlet.verAlertInitForm(alert);

}

return true;

}

public void commandAction(Command c, Displayable d) {

Aplicación Desarrollada

JXTA sobre J2ME - 75 -

String label = c.getLabel();

if (d == replyForm) {

if (label.equals("Enviar")) {

if (send()) {

midlet.verInitForm();

}

} else if (label.equals("Volver")) {

midlet.verInitForm();

}

} else if (d == confirmForm) {

if (c.getCommandType() == Command.OK) {

midlet.resetConfig();

} else {

midlet.verInitForm();

}

}

}

public void verConfirmForm()

{

midlet.display.setCurrent(confirmForm);

}

public void ponerPeerANull()

{

peer = null;

}

public void busqueda(String name)

{

try

{

if (name == null)

peer.search(PeerNetwork.PIPE, "Name", TALKNAME_PREFIX + "*", 1);

else

peer.search(PeerNetwork.PIPE,"Name",

TALKNAME_PREFIX + name, 1);}

catch(Exception e){}

}

public void establecerSelectedPipe(String pipe)

{

selectedPipe = pipe;

}

• Operaciones(Michat midlet): constructor de la clase que inicializa el atributo midlet. • sendForm(): método que prepara la página donde se escribe el mensaje a enviar. • setupConfirmForm(): es donde se crea la pagina para restablecer los valores por defecto. • send(): método donde se crea el mensaje a enviar a partir del texto introducido en la

pantalla replyForm. Se obtiene el Id de la tubería del contacto establecido en currentBuddy (para lo que se llama a un método de la clase Michat) y se envía mediante un método de la clase PeerNetwork por medio de su objeto peer.

• sendHello(String saludo): método que envía a la tubería del IP2P Group la cadena saludo. Se usa para enviar los mensajes de conexión y desconexión al NetPeerGroup.

Aplicación Desarrollada

JXTA sobre J2ME - 76 -

• initiateConnect(): método para realizar la petición de conexión para lo que pone a verdadero la variable connectInitiated.

• connect(): obtiene los datos de configuración (dirección del relay, puerto, etc.) a partir de la clase Conf a través de la clase Michat y los utiliza para conectarse al relay gracias a la clase PeerNetwork. Crea las dos tuberías donde esperamos mensajes (la del IP2P Group y la privada) y escucha en ellas también gracias a métodos de la clase PeerNetwork.

• disconnect(): cierra ambas tuberías y le da el valor de falso a la variable connected. • poll(): sondea el relay utilizando el método poll de la clase PeerNetwork para obtener los

mensajes recibidos en las dos tuberías que hemos creado. Después procesa los mensajes obteniendo el nombre del contacto que los remite así como los datos. Si el contacto no se encuentra en la lista que disponemos se añade junto con el Id de su tubería.

• commandAction(Command c, Displayable d): método donde se determinan las acciones a realizar cuando el CommandListener recoge un evento de pulsación de tecla, en este caso de los formularios que se han creado en esta clase.

• verConfirmForm(): establece como pantalla el formulario confirmForm. • ponerPeerANull(): establece el valor de peer a null. • busqueda(String name): método que realiza la búsqueda del contacto con nombre name.

Si name es igual a null, se realiza una búsqueda general. La búsqueda se realiza gracias a métodos de la clase PeerNetwork.

• establecerSelectedPipe(String pipe): se establece el valor pipe para la variable selectedPipe.

5.1.3. CLASE BUDDY

public class Buddy implements CommandListener Los atributos que contiene la clase Buddy son:

private static final String RECORD_STORE_NAME = "jxta-midp-chat";

private static final int BUDDYLIST_RECORD_INDEX = 2;

private static final int MAX_TEXTFIELD_SIZE = 256;

private static final boolean DEBUG = true;

private List buddyList = null;

private Form addBuddyForm = null;

private Michat midlet;

private Hashtable buddyIds;

private TextField tfBuddy = null;

• RECORD_STORE_NAME: constante que guarda el nombre del Record Store para el almacenamiento persistente (RMS).

• BUDDYLIST_RECORD_INDEX: índice para el registro reservado para el almacenamiento de la lista de contactos.

• MAX_TEXTFIELD_SIZE: máxima cantidad de caracteres que vamos a permitir en los campos TextField.

• DEBUG: constante de control para imprimir en la salida estándar. • buddyList: lista que contendrá los contactos que hayamos descubierto. • addBuddyForm: formulario para la adición de nuevos contactos a la lista.

Aplicación Desarrollada

JXTA sobre J2ME - 77 -

• midlet: objeto de la clase Michat mediante el cuál invocaremos los métodos de dicha clase.

• buddyIds: almacenará parejas de valores nombre/id sin permitir que exista ninguna repetida.

• tfBuddy: campo de texto par añadir el nombre de un nuevo contacto a buscar.

A continuación procederemos a enumerar los métodos con los que cuenta la clase Buddy: public Buddy(Michat midlet)

{

this.midlet = midlet;

buddyIds = new Hashtable();

}

public void editBuddies()

{

midlet.display.setCurrent(buddyList);

}

public void readBuddies() {

buddyList = new List("Lista de contactos", List.IMPLICIT);

Command cmdChat = new Command("Enviar privado", Command.SCREEN, 1);

Command cmdAdd = new Command("Añadir", Command.SCREEN, 2);

Command cmdDelete = new Command("Borrar", Command.SCREEN, 3);

Command cmdSearch = new Command("Buscar nuevos contactos",

Command.SCREEN, 4);

Command cmdBack = new Command("Volver", Command.BACK, 5);

buddyList.addCommand(cmdChat);

buddyList.addCommand(cmdAdd);

buddyList.addCommand(cmdDelete);

buddyList.addCommand(cmdSearch);

buddyList.addCommand(cmdBack);

buddyList.setCommandListener(this) ;

RecordStore rs = null;

try {

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, false);

byte[] data = rs.getRecord(BUDDYLIST_RECORD_INDEX);

ByteArrayInputStream bais = new ByteArrayInputStream(data);

DataInputStream dis = new DataInputStream(bais);

int size = dis.readShort();

if (DEBUG) {

System.out.println("Leyendo contactos: numero=" + size);

}

for (int i=0; i < size; i++) {

String buddy = dis.readUTF();

buddyList.append(buddy, null);

String buddyId = dis.readUTF();

buddyIds.put(buddy, buddyId);

if (DEBUG) {

System.out.println(" Contacto leido =\"" + buddy +

"\" id=\"" + buddyId + "\"");

Aplicación Desarrollada

JXTA sobre J2ME - 78 -

}

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

}

}

}

public void storeBuddies() {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

DataOutputStream dos = new DataOutputStream(baos);

RecordStore rs = null;

try {

int size = buddyList.size();

if (DEBUG) {

System.out.println("Salvando contactos: numero=" + size);

}

dos.writeShort(size);

for (int i=0; i < size; i++) {

String buddy = buddyList.getString(i);

dos.writeUTF(buddy);

String buddyId = (String) buddyIds.get(buddy);

if (buddyId == null) {

// not yet discovered

buddyId = "";

}

dos.writeUTF(buddyId);

if (DEBUG) {

System.out.println(" Contacto salvado=\"" +

buddy + "\" id=\"" + buddyId + "\"");

}

}

dos.close();

byte[] data = baos.toByteArray();

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, true);

int recordId = BUDDYLIST_RECORD_INDEX;

try {

rs.getRecord(recordId);

rs.setRecord(recordId, data, 0, baos.size());

} catch (RecordStoreException rex) {

recordId = rs.addRecord(data, 0, baos.size());

}

Aplicación Desarrollada

JXTA sobre J2ME - 79 -

if (DEBUG) {

System.out.println("Salvada lista de contactos: recordId=" +

recordId +

" numero=" + size +

" dlen=" + data.length);

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

dos.close();

baos.close();

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

}

}

}

public void chatBuddy() {

int i = buddyList.getSelectedIndex();

String buddy = buddyList.getString(i);

midlet.cambiarCurrentBuddy(buddy);

midlet.verSendForm();

}

public void addBuddy() {

addBuddyForm = new Form("Añadir contacto");

tfBuddy = new TextField("Nuevo contacto: ",

"",

MAX_TEXTFIELD_SIZE,

TextField.ANY);

addBuddyForm.append(tfBuddy);

Command cmdOK = new Command("OK", Command.OK, 1);

addBuddyForm.addCommand(cmdOK);

addBuddyForm.setCommandListener(this) ;

midlet.display.setCurrent(addBuddyForm);

}

public void deleteBuddy() {

int i = buddyList.getSelectedIndex();

buddyList.delete(i);

}

public void commandAction(Command c, Displayable d) {

String label = c.getLabel();

if (d == buddyList) {

if (c.getCommandType() == Command.BACK) {

Aplicación Desarrollada

JXTA sobre J2ME - 80 -

midlet.verInitForm();

storeBuddies();

}

if (label.equals("Enviar privado")) {

chatBuddy();

storeBuddies();

} else if (label.equals("Añadir")) {

addBuddy();

} else if (label.equals("Borrar")) {

deleteBuddy();

storeBuddies();

} else if (label.equals("Buscar nuevos contactos")) {

try {

midlet.verBusqueda(null);}

catch (Exception e) {}

midlet.verInitForm();

}

} else if (d == addBuddyForm) {

if (c.getCommandType() == Command.OK) {

String buddyAdded = tfBuddy.getString();

try {

midlet.verBusqueda(buddyAdded);}

catch (Exception e){}

midlet.verInitForm();

}

}

}

public String obtenerBuddyId(String buddy)

{

String buddyId = (String) buddyIds.get(buddy);

return buddyId;

}

public void añadirBuddy(String name, String id)

{

boolean flag = false;

int size = buddyList.size();

for (int i=0; i < size; i++) {

String buddy = buddyList.getString(i);

if (buddy.equals(name)) {

flag = true;

break;

}

}

if (!flag) {

buddyList.append(name,null);

storeBuddies();

}

buddyIds.put(name, id);

}

• Buddy(Michat midlet): contructor de la clase donde se inicia el objeto midlet de la clase

Michat. • editBuddies(): método que muestra por pantalla la lista buddyList.

Aplicación Desarrollada

JXTA sobre J2ME - 81 -

• readBuddies(): crea la lista buddyList con todos sus componentes y recupera desde el RMS la lista de contactos salvada en la ejecución anterior. Recupera asimismo los Ids de las tuberías de los contactos.

• storeBuddies(): almacena la lista de contactos en la memoria permanente. • chatBuddy(): establece como currentBuddy (atributo de la clase Michat) el contacto

seleccionado de la lista y llama al método encargado de preparar el mensaje para ese contacto.

• addBuddy(): prepara el formulario para añadir un nuevo contacto y lo establece como pantalla en ejecución.

• deleteBuddy(): elimina el contacto seleccionado de la lista de contactos. • commandAction(Command c, Displayable d): método donde se determinan las acciones

a realizar cuando el CommandListener recoge un evento de pulsación de tecla, en este caso del formulario y la lista creados en esta clase.

• obtenerBuddyId(String buddy): retorna el identificador almacenado en buddyIds para el nombre buddy.

• añadirBuddy(String name, String id): método que almacena en la lista de contactos el usuario con nombre name si no se encontraba ya en ella A su vez, almacena en buddyIds la pareja name/id.

5.1.4. CLASE CONF

public class Conf implements CommandListener La clase Conf cuenta con los atributos que procedemos a describir a continuación.

private static final String RECORD_STORE_NAME = "jxta-midp-chat";

private static final int CONFIG_RECORD_INDEX = 1;

private static final int DEFAULT_ALERT_TIMEOUT = 5000;

private static final int MAX_TEXTFIELD_SIZE = 256;

private static final String[] PIPE_IDS_CHOICES = { "PIPE 1", "PIPE 2",

"PIPE 3" };

private static final String MI_PIPEID1 = "urn:jxta:uuid-" +

"59616261646162614E50472050325033E" +

"574D38B19784F39AC91EA77E162A10604";

private static final String MI_PIPEID2 = "urn:jxta:uuid-" +

"59616261646162614E50472050325033E" +

"3CE324961BC4FD28252F82CF4BA709F04";

private static final String MI_PIPEID3 = "urn:jxta:uuid-" +

"59616261646162614E504720503250338" +

"2F310EA59804AB3880B816B06A1F4A704";

private static final boolean DEBUG = true;

private Michat midlet;

private Form configForm = null;

private TextField tfRelayHost = null;

private TextField tfRelayPort = null;

private TextField tfIdentity = null;

private TextField tfPollInterval = null;

private ChoiceGroup cgPipeId = null;

private byte[] state = new byte[0];

Aplicación Desarrollada

JXTA sobre J2ME - 82 -

• RECORD_STORE_NAME: constante que guarda el nombre del Record Store para el almacenamiento persistente (RMS).

• CONFIG_RECORD_INDEX: índice para el registro reservado para el almacenamiento de la configuración.

• DEFAULT_ALERT_TIMEOUT: constante que especifica el tiempo que deben durar las alertas.

• MAX_TEXTFIELD_SIZE: máxima cantidad de caracteres que vamos a permitir en los campos TextField.

• PIPE_IDS_CHOICES: constante que almacena los nombres de las tres posibles elecciones para la tubería privada.

• MI_PIPEID1: constante que almacena el identificador para la tubería privada 1. • MI_PIPEID2: constante que almacena el identificador para la tubería privada 2. • MI_PIPEID3: constante que almacena el identificador para la tubería privada 3. • DEBUG: constante de control para imprimir en la salida estándar. • midlet: objeto de la clase Michatque nos permitirá usar sus métodos. • configForm: formulario donde estableceremos la configuración de la aplicación. • tfRelayHost: campo de texto para la introducción de la dirección del relay. • tfRelayPort: campo de texto para la introducción del puerto del relay. • tfIdentity: campo de texto para la introducción del nick. • tfPollInterval: campo de texto para la introducción del intervalo de sondeo al relay. • cgPipeId: objeto ChoiceGroup para seleccionar la tubería para privados. • state: matriz de bytes que almacenará el valor que se devuelve al realizar una conexión

al relay y que identificará dicha conexión. Los métodos de la clase Conf son:

public Conf(Michat midlet)

{

this.midlet = midlet;

}

public void editConfig() {

if (configForm == null) {

configForm = new Form("Configuracion");

configForm.append(tfRelayHost);

configForm.append(tfRelayPort);

configForm.append(tfIdentity);

configForm.append(tfPollInterval);

configForm.append(cgPipeId);

cgPipeId.setSelectedIndex(0, true);

Command cmdOK = new Command("OK", Command.OK, 1);

Command cmdBack = new Command("Volver", Command.BACK, 2);

configForm.addCommand(cmdOK);

configForm.addCommand(cmdBack);

configForm.setCommandListener(this) ;

}

midlet.display.setCurrent(configForm);

Aplicación Desarrollada

JXTA sobre J2ME - 83 -

}

public void readConfig() {

tfRelayHost = new TextField("Host Relay: ",

"192.18.37.36",

MAX_TEXTFIELD_SIZE,

TextField.ANY);

tfRelayPort = new TextField("Puerto Relay: ",

"9700",

MAX_TEXTFIELD_SIZE,

TextField.NUMERIC);

tfIdentity = new TextField("Nick: ",

"",

MAX_TEXTFIELD_SIZE,

TextField.ANY);

tfPollInterval = new TextField("Intervalo de sondeo: ",

Integer.toString(midlet.obtenerPollInterval()),

MAX_TEXTFIELD_SIZE,

TextField.NUMERIC);

cgPipeId = new ChoiceGroup("Ids de Pipes", Choice.EXCLUSIVE,

PIPE_IDS_CHOICES,

null);

cgPipeId.setSelectedIndex(0, true);

RecordStore rs = null;

try {

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, false);

byte[] data = rs.getRecord(CONFIG_RECORD_INDEX);

ByteArrayInputStream bais = new ByteArrayInputStream(data);

DataInputStream dis = new DataInputStream(bais);

tfRelayHost.setString(dis.readUTF());

tfRelayPort.setString(dis.readUTF());

tfIdentity.setString(dis.readUTF());

tfPollInterval.setString(dis.readUTF());

try {

int interval = Integer.parseInt(tfPollInterval.getString());

midlet.establecerPollInterval(interval);

} catch (NumberFormatException ex) {

Alert alert = new Alert("Leyendo configuracion",

"Error al obtener el intervalo de sondeo: " +

tfPollInterval.getString(),null,

AlertType.WARNING);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

int stateLen = dis.readShort();

state = new byte[stateLen];

dis.readFully(state);

Aplicación Desarrollada

JXTA sobre J2ME - 84 -

if (DEBUG) {

System.out.println("Leyendo configuracion: host=" +

tfRelayHost.getString() +

" puerto=" + tfRelayPort.getString() +

" nick=" + tfIdentity.getString() +

" intervalo=" + tfPollInterval.getString()+

" stateLen=" + stateLen);

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

}

}

}

public void storeConfig() {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

DataOutputStream dos = new DataOutputStream(baos);

RecordStore rs = null;

try {

dos.writeUTF(tfRelayHost.getString());

dos.writeUTF(tfRelayPort.getString());

dos.writeUTF(tfIdentity.getString());

dos.writeUTF(tfPollInterval.getString());

dos.writeShort(state.length);

dos.write(state);

dos.close();

byte[] data = baos.toByteArray();

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, true);

int recordId = CONFIG_RECORD_INDEX;

try {

rs.getRecord(recordId);

rs.setRecord(recordId, data, 0, baos.size());

} catch (RecordStoreException rex) {

recordId = rs.addRecord(data, 0, baos.size());

}

if (DEBUG) {

System.out.println("Configuracion salvada: recordId=" +

recordId +

" host=" + tfRelayHost.getString() +

" puerto=" + tfRelayPort.getString() +

" nick=" + tfIdentity.getString() +

" intervalo=" + tfPollInterval.getString()+

" stateLen=" + state.length);

Aplicación Desarrollada

JXTA sobre J2ME - 85 -

}

try {

int interval = Integer.parseInt(tfPollInterval.getString());

midlet.establecerPollInterval(interval);

} catch (NumberFormatException ex) {

Alert alert = new Alert("Configuracion salvada",

"Error obteniendo intervalo de sondeo: " +

tfPollInterval.getString(),null,

AlertType.WARNING);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

dos.close();

baos.close();

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

}

}

}

public void commandAction(Command c, Displayable d) {

String label = c.getLabel();

if (d == configForm) {

if (c.getCommandType() == Command.OK) {

int i = cgPipeId.getSelectedIndex();

String selPipe = cgPipeId.getString(i);

if (selPipe.equals("PIPE 1"))

midlet.verEstablecerSelectedPipe(MI_PIPEID1);

else if (selPipe.equals("PIPE 2"))

midlet.verEstablecerSelectedPipe(MI_PIPEID2);

else if (selPipe.equals("PIPE 3"))

midlet.verEstablecerSelectedPipe(MI_PIPEID3);

storeConfig();

midlet.verInitForm();

} else if (c.getCommandType() == Command.BACK) {

configForm = null;

readConfig();

midlet.verInitForm();

}

}

}

public String obtenerIdentidad()

{

Aplicación Desarrollada

JXTA sobre J2ME - 86 -

return tfIdentity.getString();

}

public String obtenerDirHost()

{

return tfRelayHost.getString();

}

public String obtenerNumPort()

{

return tfRelayPort.getString();

}

public byte[] obtenerState()

{

return state;

}

public void establecerState(byte[] estado)

{

state = estado;

}

public void ponerConfigFormNull()

{

configForm = null;

}

• Conf(Michat midlet): constructor de la clase donde se inicializa el atributo midlet. • editConfig(): método donde se crea el formulario para introducir los datos de conexión y

se establece como pantalla activa. • readConfig(): se leen los datos de configuración almacenados en el Record Store. • storeConfig(): se guardan los datos sobre configuración para su posterior utilización en

el registro preparado para ello. • commandAction(Command c, Displayable d): método donde se determinan las acciones

a realizar cuando el CommandListener recoge un evento de pulsación de tecla, en este caso del formulario creado en esta clase.

• obtenerIdentidad(): método que devuelve el nick que hemos introducido en el formulario.

• obtenerDirHost(): método que devuleve la dirección del relay previamente introducida. • obtenerNumPort(): método con el que obtenemos el puerto del relay introducido

previamente en el formulario. • obtenerState(): devuelve el valor de la variable state. • establecerState(byte[] estado): cambia el valor de la variable state. • ponerConfigFormNull(): método que pone la variable configForm a null.

5.1.5. CLASE POLLTHREAD

public class PollThread extends Thread

Vamos a ver a continuación los atributos que componen la clase PollThread:

Aplicación Desarrollada

JXTA sobre J2ME - 87 -

private static final boolean DEBUG = true;

private static final int DEFAULT_ALERT_TIMEOUT = 5000;

private Michat midlet;

private Form connectingForm = null;

• DEBUG: constante de control para imprimir en la salida estándar. • DEFAULT_ALERT_TIMEOUT: constante que especifica el tiempo que deben durar las

alertas. • midlet: objeto de la clase Michat que nos permitirá el uso de los métodos de dicha clase

así como acceder al resto de clases. • connectingForm: formulario donde se creará la barra de progreso a la hora de la

conexión.

En las próximas líneas enumeraremos los métodos con los que cuenta la clase PollThread: public PollThread(Michat midlet)

{

this.midlet = midlet;

start();

}

public void run()

{

if (DEBUG) {

System.out.println("iniciando el hilo de sondeo");

}

while (!midlet.stopPolling) {

if (midlet.verCondConnect()) {

Form connectingForm = new Form("Conectando...");

StringItem elapsed = new StringItem("", null);

connectingForm.append(elapsed);

Gauge status = new Gauge(null, false, 10, 0);

connectingForm.append(status);

midlet.display.setCurrent(connectingForm);

StatusUpdate updater = new StatusUpdate(elapsed, status);

new Timer().scheduleAtFixedRate(updater, 1000, 1000);

try {

midlet.conectar();

updater.cancel();

midlet.verInitForm();

connectingForm = null;

} catch (Exception ex) {

updater.cancel();

Alert alert = new Alert("Conectando",

"Error conectando al relay: " +

ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(Alert.FOREVER);

midlet.verAlertInitForm(alert);

}

//envio los mensajes pendientes

try {

Aplicación Desarrollada

JXTA sobre J2ME - 88 -

String saludo = " has joined NetPeerGroup\n";

midlet.saludar(saludo);

midlet.verBusqueda(null);

}catch (Exception e ) {}

if (midlet.obtenerSendPending()) {

try {

midlet.enviar();

} finally {

midlet.establecerSendPending(false);

}

}

}

try {

// mientras existan mensajes, sigo sondeando el relay

while (midlet.sondear()) { }

}catch (Throwable t) {

Alert alert = new Alert("Sondeando",

"Error procesando el mensaje: " + t.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

midlet.dormirHilo();

}

if (DEBUG) {

System.out.println("paro el hilo");

}

}

• PollThread(Michat midlet): constructor de la clase donde se inicializa el atributo midlet

y se llama al método de arranque del hilo start(). • run(): método característico de los hilos, donde se especifican las acciones a realizar

cada vez que se ejecuta el hilo. En este caso, su principal función será sondear el relay periódicamente. Para ello utiliza el método poll de la clase NetPeerwork. Pr otra parte, si se ha solicitado la conexión pero aún no se ha realizado, se tratará de conectar al relay. Para ello implementará una barra de progreso en el formulario connectingForm que contará los segundos que tarda en realizarse la conexión y llamará al método connect de la clase PeerNetwork a través de la clase Michat. Al conectarse, se enviará el mensaje de conexión al NetPeerGroup y se remitirán los mensajes pendientes de envío.

5.1.6 CLASE STATUSUPDATE

public class StatusUpdate extends TimerTask

Aplicación Desarrollada

JXTA sobre J2ME - 89 -

Los atributos que utiliza la clase son:

private StringItem elapsed = null;

private Gauge status = null;

private int tick = 0;

private int max = 0;

• elapsed: variable que mostrará por pantalla el número de segundos transcurridos desde que se solicitó la conexión.

• status: es la barra de progreso en sí. Irá incrementándose desde 0 hasta el valor max. • tick: variable que almacena el número de segundos. • max:: es donde se guarda el rango máximo que alcanzará el gauge.

Los métodos de la clase StatusUpdate son los siguientes:

StatusUpdate(StringItem elapsed, Gauge status)

{

this.elapsed = elapsed;

this.status = status;

max = status.getMaxValue();

}

public void run()

{

elapsed.setText(Integer.toString(++tick) + "s");

status.setValue(tick % max);

}

• StatusUpdate(StringItem elapsed, Gauge status): constructor de la clase que inicializa los atributos elapsed, status y max.

• run(): método que se llama cada vez que el objeto de la case Timer produce un evento. Su función es actualizar la barra de progreso.

5.2. DESCRIPCIÓN DE LA APLICACIÓN

En este apartado vamos a comentar el funcionamiento de la aplicación JXME que se ha elaborado para el presente proyecto fin de carrera (Figura 5.1).

Se trata de una aplicación de mensajería instantánea o chat que implementa los protocolos JXTA, ejecutable en cualquier dispositivo móvil que cumpla las especificaciones del perfil MIDP y la configuración CLDC. Para ello, será necesario que se conecte a un relay JXTA por medio de una conexión HTTP, que actuará de intermediario recibiendo los mensajes dirigidos al móvil. El móvil para obtenerlos, sondeará el relay periódicamente.

En primer lugar, antes de conectarnos, habrá que configurar la dirección y el puerto del relay al que vamos a conectarnos, el nick o identificador que queremos utilizar, el intervalo de sondeo del relay en segundos y podremos elegir entre tres tuberías o pipes, que servirán para que el móvil tenga una tubería de entrada para recibir privados. Si no es la primera vez que utilizamos

Aplicación Desarrollada

JXTA sobre J2ME - 90 -

la aplicación, estos datos estarán almacenados de la ejecución anterior en la memoria persistente del móvil. Si es la primera vez, habrá unos valores por defecto. Para acceder a la configuración, habrá que pulsar la tecla donde pone “Menú” y elegir en el menú desplegable la opción “Configuración” (Figura 5.2). Los datos introducidos se guardarán en la memoria permanente del terminal (RMS).

Figura 5.1: Midlet Michat Figura 5.2: Página de configuración

Las opciones en el menú (Figura 5.3) son:

• “Enviar”: con lo que podremos enviar un mensaje a la tubería del IP2P Group (Instant

Point To Point Group) y que podrán escuchar todas aquellas aplicaciones de chat que escuchen en dicha tubería, incluida nuestra propia aplicación.

• “Conectar”: debe ser el paso posterior a la configuración. El móvil tratará de conectarse al relay especificado anteriormente (Figura 5.4).

• “Lista de Contactos”: una vez realizada la conexión, lo primero que se realiza es una búsqueda usuarios que hayan publicado un advertisement de tubería apropiada para la mensajería instantánea de JXTA. Los contactos hallados aparecerán en esta lista y será posible mandarles un privado (Figura 5.5). La lista de contactos tendrá a su vez una tecla con la palabra “Menú” que dispondrá de las siguientes opciones (Figura 5.6):

o “Enviar privado”: al contacto seleccionado. o “Añadir”: se envía una búsqueda de un usuario con el nick que

especifiquemos. La búsqueda, si es positiva, devolverá el identificador de la tubería para privados de dicho usuario, apareciendo su nombre en la lista de contactos.

o “Borrar”: elimina el contacto seleccionado.

Aplicación Desarrollada

JXTA sobre J2ME - 91 -

o “Buscar nuevos contactos”: inicia una nueva búsqueda de contactos que hayan publicado una tubería.

• “Configuración”: como ya hemos visto, hay que establecer la dirección y el puerto del relay, nuestro nick, el intervalo de sondeo y la tubería para privados que hemos decidido usar. Esta elección de tuberías permite que distintos usuarios de nuestra aplicación posean una tubería propia eligiendo un número distinto a la hora de la configuración. Hemos tomado tres tuberías aunque se podría aumentar para dar servicio a más usuarios.

• “Opciones por defecto”: se restauran las opciones por defecto del menú de configuración y se borra la lista de contactos.

Figura 5.3: Menú de opciones Figura 5.4: Pantalla de progreso de conexión

Al conectarnos y desconectarnos, enviaremos un mensaje a la tubería del IP2P Group indicando que nos hemos unido y que hemos abandonado el NetPeerGroup (el grupo JXTA al que se conectan todos los nodos por defecto) respectivamente (Figura 5.7).

Nuestra aplicación, por lo tanto, esperará mensajes en dos tuberías: la del IP2P Group (nosotros enviaremos también mensajes en esa tubería) y nuestra propia tubería, donde sólo escucharemos nosotros. Podremos identificar los mensajes recibidos en cada tubería ya que los recibidos en la tubería IP2P aparecerán en pantalla, aparte de con el remitente, con una arroba más la palabra IP2PGRP (Figura 5.8).

Aplicación Desarrollada

JXTA sobre J2ME - 92 -

Figura 5.5: Lista de contactos Figura 5.6: Opciones de la lista de contactos

Figura 5.7: Mensaje de conexión Figura 5.8: Mensaje enviado a la tubería IP2P

Instalación y Pruebas

JXTA sobre J2ME - 93 -

CAPÍTULO 6º: INSTALACIÓN Y PRUEBAS 6.1. INTRODUCCIÓN

En el presente capítulo vamos a proceder a enumerar los pasos necesarios para la instalación y posterior ejecución de la aplicación de mensajería instantánea. Antes que nada se comentarán tanto el hardware sobre el que se ha ejecutado como las herramientas utilizadas:

• Ordenador personal con procesador Intel Pentium III a 1000 Mhz con 256 Mb de

memoria RAM. • Sistema Operativo Windows XP con SP1. • Conexión a Internet mediante cable-módem a 128 Kbits. • Java 2 SDK, SE v1.4.2. • J2ME Wireless Toolkit 2.1. • Apache Tomcat 5.0. • WinCVS 1.3. • Ant 1.6.

6.2. INSTALACIÓN DE LA APLICACIÓN

En este apartado, en primer lugar, vamos a ver las etapas básicas que se han realizado para

la construcción de nuestro MIDlet:

1. Desarrollo: en esta fase vamos a escribir el código que conforma nuestro MIDlet. 2. Compilación: se compilará nuestra aplicación haciendo uso de un compilador J2SE, en

nuestro caso el Java 2 SDK, SE v1.4.2 que nos habremos descargado desde la página http://java.sun.com/j2se/1.4.2/download.html.

3. Preverificación: antes de empaquetar nuestro MIDlet es necesario realizar un proceso de preverificación de las clases Java. En esta fase se realiza un examen del código del MIDlet para ver que no viola ninguna restricción de seguridad de la plataforma J2ME.

4. Empaquetamiento: en esta fase crearemos tanto un archivo JAR que contiene los recursos que usa nuestra aplicación, como un archivo descriptor JAD.

5. Ejecución: para esta fase haremos uso de los emuladores que nos permitirán ejecutar nuestro MIDlet.

6. Depuración: esta última fase nos permitirá depurar los fallos detectados en la fase anterior de nuestro MIDlet.

Instalación y Pruebas

JXTA sobre J2ME - 94 -

El proceso de instalación de todo lo necesario para la ejecución de los MIDlets se puede realizar básicamente de dos formas:

• A través de la línea de comandos, • Utilizando una serie de herramientas, tanto para la descarga de los componentes

necesarios, como para su compilación, preverificación, empaquetamiento y ejecución. Este es el método que se ha utilizado.

6.2.1. INSTALACIÓN EN LÍNEA DE COMANDOS

• Lo primero que haremos será instalar el SDK de J2SE. Una vez instalado añadiremos a nuestro path la carpeta de la instalación y crearemos una nueva variable de entorno llamada JAVA_HOME con la ruta de dicha carpeta.

• A continuación instalaremos las APIs de CLDC y de MIDP. Al igual que hicimos con anterioridad tendremos que añadir la dirección donde instalemos dichas APIs a la variable path y crear una nueva variable de entorno MIDP_HOME con la ruta de esa carpeta.

• Posteriormente compilaremos usando la herramienta javac, incluyendo en el classpath

tanto las librerías del perfil y la configuración, como las que contienen los protocolos JXTA que nos habremos bajado de la página www.jxta.org. También habrá que incluir las clases con las que implementaremos la conexión con el relay (clase PeerNetwork, Message, etc.).

• Tras esto realizaremos la preverificación de clases usando la herramienta preverify. Aquí también debemos incluir en el classpath las librerías de la CLDC y del MIDP.

• Por último empaquetaremos el MIDlet dejándolo totalmente preparado para su descarga en el dispositivo MID. Para ello tendremos que construir el archivo JAR con los ficheros que forman el MIDlet y un archivo descriptor de la aplicación con extensión JAD. Antes deberemos construir el archivo MANIFEST.MF que contiene una serie de campos que después serán utilizados para crear el descriptor y que será incluido en el archivo JAR. Para crear éste utilizaremos la herramienta jar. En la Tabla 6.1. vemos el archivo MANIFEST.MF para nuestra aplicación.

MIDlet-1: Michat, Michat.png, net.jxta.midp.demo.michat.Michat

MIDlet-Name: Michat

MIDlet-Vendor: J.P.Soriano Canela

MIDlet-Version: 1.0

MicroEdition-Configuration: CLDC-1.0

MicroEdition-Profile: MIDP-1.0

Tabla 6.1: Archivo MANIFEST.MF • A partir del manifiesto creamos el descriptor JAD que en nuestra aplicación es el

siguiente:

Instalación y Pruebas

JXTA sobre J2ME - 95 -

MIDlet-1: Michat, Michat.png, net.jxta.midp.demo.michat.Michat

MIDlet-Jar-Size: 29413

MIDlet-Jar-URL: michat.jar

MIDlet-Name: Michat

MIDlet-Vendor: J.P.Soriano Canela

MIDlet-Version: 1.0

Tabla 6.2: Archivo Michat.jad • Para la ejecución y la depuración deberemos utilizar una herramienta de emulación del

dispositvo MID. 6.2.2. INSTALACIÓN USANDO CVS Y ANT

• En primer lugar, suponemos instalado ya el entorno de programación Java 2 SDK, SE v1.4.2. Con él se proporcionan las librerías Java necesarias y las herramientas para compilar las clases de la aplicación.

• A continuación crearemos una nueva variable de entorno MIDP_HOME con la ruta de la carpeta donde se encuentren las APIs de CLDC y de MIDP.

• Posteriormente, será necesario descargarnos de la página de JXTA, tanto el proyecto platform (de donde obtendremos tras compilarlo la librería jxta.jar) como el proyecto jxme/midp. Para la descarga utilizaremos el programa WinCVS, y utilizaremos las intrucciones que se nos proporcionan en la página http://jxme.jxta.org/build.html.

• Una vez descargado el proyecto jxme/midp, como un componente más y siguiendo la estructura que tienen el resto de componentes, introduciremos nuestra aplicación michat. Debemos modificar el fichero build.xml que viene en jxme/midp para introducir el componente michat y añadir la ruta donde van a estar las librerías CLDC, MIDP y jxta.jar en el classpath. Podríamos eliminar el resto de acciones que ejecuta sobre los demas componentes. Debemos crear otro fichero build.xml dentro de michat. Ejemplos de los dos build.xml modificados se adjuntan a continuación en las tablas 6.3 y 6.4.

• También debemos crear un archivo MANIFEST.MF y un descriptor michat.jad como vimos antes, que guardaremos en la carpeta jxme/midp/demo/michat/bin.

• Para compilar, utilizaremos la herramienta Ant que actuará sobre los build.xml antes mencionados. El Ant realizará, no sólo la compilación, sino también la preverificación y el empaquetado, obteniendo como resultado en la carpeta dist de jxme/midp/demo/ michat, tanto el archivo jad como el jar listos para su ejecución en el emulador.

• Se podría ejecutar directamente en el emulador, utilzando la orden ant run a la hora d compilar. Para ello es necesario haber instalado anteriormente la herramienta de emulación J2ME Wireless Toolkit 2.1. Podemos descargarnosla desde la página: http://java.sun.com/products/j2mewtoolkit/download-2_1.html.

<?xml version="1.0" encoding="UTF-8"?> <project basedir="." default="dist" name="jxta-midp"> <property environment="env"/> <property name="midp_lib" value="${env.MIDP_HOME}/lib/midpapi10.jar;${env.MIDP_HOME}/lib/cldcapi10.jar;${env.MIDP_HOME}/lib/jxta.jar"/> <property name="preverifier" value="${env.MIDP_HOME}/bin/preverify"/>

Instalación y Pruebas

JXTA sobre J2ME - 96 -

<property name="src" value="src"/> <property name="test" value="test"/> <property name="classes_cldc_unverified" value="classes_cldc_unverified"/> <property name="classes_cldc_verified" value="classes_cldc"/> <property name="dist" value="dist"/> <property name="javadoc" value="doc"/> <property name="res" value="res"/> <property name="debug" value="off"/> <property name="test_classes" value="test/classes"/> <property name="tools_classes" value="tools/classes"/> <!-- build the ant tasks that we need and install them into the correct directory --> <target name="tools"> <mkdir dir="${tools_classes}"/> <javac destdir="${tools_classes}" srcdir="tools" target="1.1"/> <jar jarfile="${env.ANT_HOME}/lib/jxta-tools.jar"> <fileset dir="${tools_classes}"/> </jar> <delete dir="${tools_classes}"/> </target> <target name="prepare"> <tstamp/> <mkdir dir="${classes_cldc_unverified}"/> <mkdir dir="${classes_cldc_verified}"/> <mkdir dir="${dist}"/> </target> <target name="test_prepare"> <mkdir dir="${test_classes}"/> </target> <target depends="prepare" name="compile_cldc"> <echo message="Disabling Doja..."/> <javac destdir="${classes_cldc_unverified}" srcdir="${src}" bootclasspath="${midp_lib}" debug="${debug}" includeAntRuntime="false" target="1.1"> <include name="net/jxta/j2me/*"/> <include name="net/jxta/j2me/cldc/*"/> </javac> <echo message="Preverifying classes..."/> <exec dir="." executable="${preverifier}" failonerror="true"> <arg line = "-classpath ${midp_lib}"/> <arg line = "-d ${classes_cldc_verified}"/> <arg line = "${classes_cldc_unverified}"/> </exec> </target>

Instalación y Pruebas

JXTA sobre J2ME - 97 -

<target depends="compile_cldc" name="dist_cldc"> <jar jarfile="${dist}/jxta-cldc.jar"> <fileset dir="${classes_cldc_verified}"/> </jar> <ant inheritAll="false" dir="demo/michat" target="dist"/> </target> <target depends="dist_cldc, dist_cdc" name="dist"/> <target name="javadoc"> <mkdir dir="${javadoc}"/> <javadoc sourcepath="${src};${tutorial}/src" classpath="${midp_lib}" access="public" windowtitle="JXTA for J2ME" doctitle="JXTA for J2ME" header="JXTA for J2ME" destdir="${javadoc}"> <group title="API" packages="net.jxta.*"/> <group title="Examples" packages="tutorial.*"/> <package name="net.jxta.j2me"/> <package name="tutorial"/> </javadoc> </target> <target depends="dist" name="michat"> <ant inheritAll="false" dir="demo/michat" target="run"/> </target> <target depends="compile_cldc, test_prepare" name="compile_test"> <javac destdir="${test_classes}" classpath="${classes_cldc_unverified}" srcdir="${test}" debug="${debug}" includeAntRuntime="false" target="1.1"/> </target> <target depends="compile_test" name="test"> <java classname="net.jxta.j2me.MessageTest" fork="true" dir="${test}" classpath="${classes_cldc_unverified}:${test_classes}"/> </target> <target name="clean"> <delete dir="${classes_cldc_unverified}"/> <delete dir="${classes_cldc_verified}"/> <delete dir="${dist}"/> <delete dir="${javadoc}"/> <delete dir="${test_classes}"/> <ant inheritAll="false" dir="demo/michat" target="clean"/> </target> </project> tabla 6.3: build.xml para proyecto jxme/midp

Instalación y Pruebas

JXTA sobre J2ME - 98 -

<?xml version="1.0" encoding="UTF-8"?> <project basedir="." default="dist" name="jxta-midp-michat-demo"> <!-- if you get a taskdef not found error, do an 'ant tools' at the top level --> <taskdef name="jad" classname="net.jxta.j2me.tools.Jad"/> <property environment="env"/> <property name="midp_lib" value="${env.MIDP_HOME}/lib/midpapi10.jar;${env.MIDP_HOME}/lib/cldcapi10.jar;${env.MIDP_HOME}/lib/jxta.jar"/> <property name="preverify_exec" value="${env.MIDP_HOME}/bin/preverify"/> <property name="emulator_exec" value="${env.MIDP_HOME}/bin/emulator"/> <!-- needed to build midp4palm --> <property name="converter_jar" value="${env.MIDP4PALM_HOME}/Converter/Converter.jar"/> <property name="project" value="michat"/> <property name="src" value="src"/> <property name="build" value="classes_unverified"/> <property name="preverify" value="classes"/> <property name="dist" value="dist"/> <property name="lib" value="lib"/> <property name="bin" value="bin"/> <property name="javadoc" value="doc"/> <property name="res" value="res"/> <property name="debug" value="off"/> <property name="jxta_lib" value="../../dist/jxta-cldc.jar"/> <target name="prepare"> <tstamp/> <mkdir dir="${build}"/> <mkdir dir="${preverify}"/> <mkdir dir="${dist}"/> <mkdir dir="${lib}"/> </target> <target depends="prepare" name="prepare_lib"> <copy file="${jxta_lib}" todir="${lib}"/> </target> <target depends="prepare_lib" name="compile"> <javac destdir="${build}" srcdir="${src}" classpath="${jxta_lib}" bootclasspath="${midp_lib}" debug="${debug}" target="1.1"/> <echo message="Preverifying..."/> <exec dir="." executable="${preverify_exec}"> <arg line = "-classpath ${midp_lib}"/> <arg line = "-d ${preverify}"/> <arg line = "${build}"/> </exec> </target>

Instalación y Pruebas

JXTA sobre J2ME - 99 -

<target depends="compile" name="dist"> <unjar src="${jxta_lib}" dest="${preverify}"/> <!-- delete jxta-midp's manifest --> <delete dir="${preverify}/META-INF"/> <jar basedir="${preverify}" jarfile="${dist}/${project}.jar" manifest="${bin}/MANIFEST.MF"> <fileset dir="${res}"/> </jar> <copy file="${bin}/${project}.jad" todir="${dist}"/> <jad jar="${dist}/${project}.jar" jad="${dist}/${project}.jad"/> </target> <target depends="dist" name="run"> <exec dir="." executable="${emulator_exec}" failonerror="true"> <arg line = "-classpath ${dist}/${project}.jar"/> <arg line = "-Xdescriptor:${dist}/${project}.jad"/> <arg line = "-Xdevice:DefaultColorPhone"/> </exec> </target> <target depends="dist" name="midp4palm"> <java classname="com.sun.midp.palm.database.MakeMIDPApp" fork="true" dir="${dist}" classpath="${converter_jar}"> <!-- use JXTA for the creator for now --> <arg line = "-creator JXTA -jad ${project}.jad ${project}.jar"/> </java> </target> <target name="clean"> <delete dir="${build}"/> <delete dir="${preverify}"/> <delete dir="${dist}"/> <delete dir="${lib}"/> </target> </project> tabla 6.4: build.xml para proyecto jxme/midp/demo/michat 6.2.3. DESCARGA VÍA OTA

Una vez ya tenemos tanto el archivo descriptor JAD como el archivo JAR, procederemos a su descarga al móvil. Para ello debemos contar con un servidor web desde el que descargarnos la aplicación.

Para crear el servidor, en el presente proyecto utilizamos el Apache Tomcat 5.0 que nos lo

descargaremos desde la página http://jakarta.apache.org/site/binindex.cgi. Lo instalamos

Instalación y Pruebas

JXTA sobre J2ME - 100 -

utilizando el ejecutable que nos hemos bajado. En la instalación debemos indicarle dónde está situada la carpeta del Java 2 SDK, SE v1.4.2.

Una vez instalado, miraremos en el archivo web.xml de la carpeta conf si se encuentran

incluidos los tipos MIME para los archivos JAD y JAR como vimos en el apartado 3.4.4.2. Crearemos una página de inicio que colocaremos en la carpeta webapps\ROOT junto con los archivos JAD y JAR. En la Tabla 6.5 vemos el código de dicha página web:

<html>

<head>

<title>PFC</title>

</head>

<body>

PFC JXTA para J2ME

<p>

Descargar midlet:

<p>

<a href="http://212.79.156.200:8080/michat.jad">michat.jad</a>

</body>

</html>

Tabla 6.5: Código de index.html

Dentro del descriptor debemos cambiar el campo MIDlet-Jar-URL y actualizarlo con la

nueva ubicación del fichero JAR. El nuevo fichero JAD lo vemos en la Tabla 6.6:

MIDlet-1: Michat, Michat.png, net.jxta.midp.demo.michat.Michat

MIDlet-Jar-Size: 29413

MIDlet-Jar-URL: http://212.79.156.200:8080/michat.jar

MIDlet-Name: Michat

MIDlet-Vendor: J.P.Soriano Canela

MIDlet-Version: 1.0

Tabla 6.6: Archivo Michat.jad Hecho esto, echaremos a andar el servidor por medio de la orden startup. En este momento

ya podremos acceder a la dirección http://212.79.156.200:8080 (la dirección IP del equipo sobre el que hemos montado el servidor) desde el móvil y proceder a la descarga del MIDlet desde el enlace que se provee. Para echar abajo el servidor se usará la instrucción shutdown.

Realizado esto, nos saldrá en pantalla el nombre de la aplicación, junto con su tamaño, la

versión y el nombre del creador. Deberemos aceptar para que comience la descarga al móvil. Este proceso durará cierto tiempo. Por último, nos saldrá una pantalla informando de que el MIDlet se ha descargado correctamente y en qué lugar se ha almacenado, pudiéndolo ejecutar cuando queramos.

Instalación y Pruebas

JXTA sobre J2ME - 101 -

6.3. PRUEBAS DE LA APLICACIÓN

Para la aplicación que acompaña al presente proyecto, se han hecho una serie de pruebas en el emulador antes mencionado, el J2ME Wireless Toolkit.

Como vimos antes, la herramienta J2ME Wireless Toolkit cuenta con un emulador en el que

se pueden hacer las pruebas pertinentes sin tener que descargar el MIDlet al dispositivo móvil. Una vez contamos ya con los archivos JAR y JAD de la aplicación Michat, utilizamos la

opción descarga vía OTA que se adjunta con el emulador para imitar dicha descarga en el móvil. Para conseguirlo, se le pasa la dirección http desde la que queremos bajarlo y realiza todos los pasos propios de una descarga OTA.

Cuando ya tenemos la aplicación en el emulador, iniciamos su ejecución, procediendo de la

forma ya vista en el apartado 5.2 cuando se describía el funcionamiento del chat. Llegados a este punto, las pruebas realizadas han sido:

• Comunicación entre varias instancias de nuestra aplicación: se han hecho pruebas

abriendo tres instancias y comunicándolas entre ellas, tanto a través de la tubería IP2P como enviándose privados. Las tres instancias se conectan al mismo relay, el situado en la dirección http://192.18.37.36:9700, y que se suministra por defecto con nuestra aplicación. Todas las pruebas en este sentido han resultado satisfactorias.

• Comunicación entre una instancia de la aplicación y otro programa de chat JXTA, en este caso el Instant P2P también conocido como MyJXTA, descargado de la página www.jxta.org y ejecutado en un ordenador personal. Para esta prueba se ha comprobado que existe a veces un gran retardo en el envío de mensajes, pudiendo ocurrir que algunos mensajes no lleguen a su destino. De aquí se deduce que la red JXTA todavía está en una fase de evolución.

• Comunicación desde la aplicación con usuarios de otras aplicaciones de chat JXTA: en especial se ha mantenido conversaciones con una persona de nick gonzo que es programador de Sun y responsable de la aplicación de chat MyJXTA2, a través de la que hablaba y que se encuentra disponible para su descarga en la página de JXTA.

Planificación Temporal

JXTA sobre J2ME - 102 -

CAPÍTULO 7º: PLANIFICACIÓN TEMPORAL

El desarrollo de este proyecto se puede dividir en tres fases principales: • Una primera fase en la que se ha desarrollado la busqueda y el estudio del material

necesario para el desarrollo del trabajo. En esta fase se ha obtenido información acerca de los protocolos JXTA, de la plataforma J2ME y del proyecto JXME, así como de otros aspectos que ha tocado el proyecto tales como XML, OTA, etc. También se ha recabado información acerca de las tecnologías y herramientas que ha utilizado el proyecto entre las que podemos nombrar Java, J2ME Wireless Toolkit, Apache Tomcat, etc. Esta fase ha comprendido desde que se aceptó el proyecto a mediados de Noviembre de 2003 hasta el momento en que comenzó la segunda fase a principios de Febrero de 2004.

• Una segunda fase en la que se ha desarrollado la aplicación descrita en esta memoria y

las pruebas realizadas para comprobar su correcto funcionamiento. Esta fase comenzó a principios de Febrero de 2004 y se ha superpuesto en sus últimos aspectos con la tercera fase, prolongándose hasta finales de Julio de 2004.

• Una tercera fase en la que se ha realizado la memoria del trabajo desarrollado en este

proyecto. La tercera fase se comenzó a finales de Julio de 2004, terminando con la entrega final del proyecto a mediados de Septiembre de 2004.

Conclusiones

JXTA sobreJ2ME - 103 -

CAPÍTULO 8º: CONCLUSIONES

En este capítulo veremos las conclusiones obtenidas tras la realización de este proyecto, así como expondremos las posibles líneas futuras de avance relacionadas con los temas vistos.

El objetivo del presente proyecto era la creación y la posterior prueba de una aplicación

basada en la versión con proxy del proyecto JXME. Dicha aplicación se adjunta en la presente documentación e implementa una aplicación de mensajería instantánea o chat. Se detalla su funcionamiento en el apartado 5.2.

Tras desarrollarla y compilarla, ha sido probada con éxito en el emulador proporcionado por

Sun Microsystems, tal y como se ha comentado anteriormente en el apartado 6.3. De las diversas pruebas obtenemos como conclusión, que la tecnología JXTA se encuentra

aún en una fase muy primitiva de desarrollo, no estando ni mucho menos optimizada. Se propone como posible línea futura de estudio el probar la aplicación en términales

móviles reales, pudiendo servir cualquier dispositivo con capacidad para ejecutar aplicaciones Java y que cumpla ya sea con el perfil MIDP 1.0 o con el MIDP 2.0.

Otra posible línea de avance sería eliminar completamente la figura del relay como

intermediario entre la red JXTA y el móvil, dejando que éste gestione los advertisements, añadiéndole parsers XML, y que realice operaciones de búsqueda, creación de grupos y tuberías, etc. Esto coincide con la versión sin proxy, versión para la que ya se han dado algunos pasos entre los componentes del proyecto JXTA.

Por último, podría contemplarse la posibilidad de crear otras aplicaciones con distintas funcionalidades, amparándose en los distintos recursos que nos proporciona los protocolos JXTA.

Bibliografía

JXTA sobreJ2ME - 104 -

CAPÍTULO 9º: BIBLIOGRAFÍA

Lenguaje Java: • Java 2: Curso de Programación. Fco. Javier Ceballos. Editorial Ra-Ma. JXTA y JXME: • Project JXTA v2.0: Java Programmer´s Guide. Sun Microsystems. • http://www.jxta.org/ • http://jxme.jxta.org/ J2ME y MIDP: • Java a Tope: J2ME. Sergio Gálvez Rojas y Lucas Ortega Díaz. Universidad de Málaga. • http://developers.sun.com/techtopics/mobility/learn/midp/getstart/

• http://developers.sun.com/techtopics/mobility/learn/midp/midp20/

• http://developers.sun.com/techtopics/mobility/midp/articles/midpwap/

J2ME Wireless Toolkit: • http://developers.sun.com/techtopics/mobility/midp/articles/wtoolkit/

Apache Tomcat: • http://jakarta.apache.org/tomcat/tomcat-5.0-doc/index.html

OTA: • http://developers.sun.com/techtopics/mobility/midp/articles/ota/index.h

tml

XML: • http://developers.sun.com/techtopics/mobility/midp/articles/parsingxml/

• http://developers.sun.com/dev/evangcentral/totallytech/j2me.html

Código de la Aplicación

JXTA sobre J2ME - 105 -

CAPÍTULO 10º:

CÓDIGO DE LA APLICACIÓN

/****************************************************************************

* La clase Michat es la clase principal de la aplicación al ser la que *

* extiende a la clase MIDlet. Por lo tanto, implementará los métodos propios*

* de un MIDlet: startApp(), pauseApp() y destroyApp(). Se encarga de crear *

* la página principal que contiene el menú de opciones. Para efectuar la *

* acción correspondiente a la opción elegida, la clase implementa *

* CommandListener, que detecta el evento de pulsar una tecla. También actúa *

* de intermediaria entre el resto de clases, contando para ello con métodos *

* públicos que invocarán a su vez a métodos de las demás clases. *

* *

****************************************************************************/

package net.jxta.midp.demo.michat;

import javax.microedition.lcdui.Display;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.StringItem;

import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.AlertType;

import javax.microedition.rms.RecordStore;

import javax.microedition.rms.RecordStoreException;

import javax.microedition.midlet.MIDlet;

public final class Michat extends MIDlet

implements CommandListener {

private static final int DEFAULT_POLL_INTERVAL = 1;

private static final int DEFAULT_SCROLL = 3;

private static final String RECORD_STORE_NAME = "jxta-midp-chat";

private static final boolean DEBUG = true;

private static final String INSTANTP2P_GROUPNAME = "IP2PGRP";

protected Display display = null;

private Form initForm = null;

private String currentBuddy = null;

private int pollInterval = DEFAULT_POLL_INTERVAL;

private PollThread pollThread = null;

protected boolean stopPolling = false;

private boolean sendPending = false;

Código de la Aplicación

JXTA sobre J2ME - 106 -

private Buddy buddy;

private Conf config;

private Operaciones op;

public Michat() {

initForm = new Form("JXTA Chat");

buddy = new Buddy(this);

config = new Conf(this);

op = new Operaciones(this);

buddy.readBuddies();

config.readConfig();

op.setupConfirmForm();

Command cmdSend = new Command("Enviar", Command.SCREEN, 1);

Command cmdConnect = new Command("Conectar", Command.SCREEN, 2);

Command cmdBuddies = new Command("Lista de contactos",

Command.SCREEN, 3);

Command cmdConfig = new Command("Configuracion", Command.SCREEN, 4);

Command cmdDefault = new Command("Opciones por defecto",

Command.SCREEN, 5);

Command cmdExit = new Command("Exit", Command.EXIT, 6);

initForm.addCommand(cmdSend);

initForm.addCommand(cmdConnect);

initForm.addCommand(cmdBuddies);

initForm.addCommand(cmdConfig);

initForm.addCommand(cmdDefault);

initForm.addCommand(cmdExit);

initForm.setCommandListener(this);

}

public void commandAction(Command c, Displayable d) {

if (c.getCommandType() == Command.EXIT) {

destroyApp(true);

notifyDestroyed();

return;

}

String label = c.getLabel();

if (d == initForm) {

if (label.equals("Enviar")) {

currentBuddy = INSTANTP2P_GROUPNAME;

verSendForm();

} else if (label.equals("Configuracion")) {

config.editConfig();

} else if (label.equals("Lista de contactos")) {

buddy.editBuddies();

} else if (label.equals("Conectar")) {

op.initiateConnect();

} else if (label.equals("Opciones por defecto")) {

op.verConfirmForm();

}

}

}

Código de la Aplicación

JXTA sobre J2ME - 107 -

public void startApp() {

display = Display.getDisplay(this);

// inicio la pagina de configuracion si no esta hecho

if ("".equals(config.obtenerIdentidad())) {

config.editConfig();

} else {

display.setCurrent(initForm);

}

stopPolling = false;

pollThread = new PollThread(this);

}

public void pauseApp() {

stopPolling = true;

pollThread = null;

}

public void destroyApp(boolean unconditional) {

try {

op.sendHello(" has left NetPeerGroup"); }

catch (Exception e) {}

stopPolling = true;

pollThread = null;

config.storeConfig();

buddy.storeBuddies();

op.ponerPeerANull();

}

public void resetConfig() {

try {

saludar(" has left NetPeerGroup");}

catch(Exception e) {}

if (op.connected) {

op.disconnect();

}

op.ponerPeerANull();

byte[] st = new byte[0];

config.establecerState(st);

try {

RecordStore.deleteRecordStore(RECORD_STORE_NAME);

} catch (RecordStoreException ex) {

if (DEBUG) {

System.out.println(ex);

}

}

config.ponerConfigFormNull();

buddy.readBuddies();

config.readConfig();

Código de la Aplicación

JXTA sobre J2ME - 108 -

currentBuddy = null;

config.editConfig();

}

public void verInitForm()

{

display.setCurrent(initForm);

}

public void verAlertInitForm(Alert alert)

{

display.setCurrent(alert,initForm);

}

public void cambiarCurrentBuddy(String cBuddy)

{

currentBuddy = cBuddy;

}

public String obtenerCurrentBuddy()

{

return currentBuddy;

}

public int obtenerPollInterval()

{

return pollInterval;

}

public void establecerPollInterval(int interval)

{

pollInterval = interval;

}

public void verEstablecerSelectedPipe(String sp)

{

op.establecerSelectedPipe(sp);

}

public String obtenerIdent()

{

return config.obtenerIdentidad();

}

public String obtenerHost()

{

return config.obtenerDirHost();

}

public String obtenerPort()

{

return config.obtenerNumPort();

}

public String obtenerCurrentBuddyId()

{

Código de la Aplicación

JXTA sobre J2ME - 109 -

return buddy.obtenerBuddyId(currentBuddy);

}

public boolean verCondConnect()

{

if (!op.connected && op.connectInitiated)

return true;

else return false;

}

public void dormirHilo()

{

try{

pollThread.sleep(pollInterval*1000);}

catch(Exception e){}

}

public boolean conectar() throws Exception

{

try {

op.connect();

return true;}

catch (Exception e) {

return false;}

}

public void saludar(String saludo)

{

op.sendHello(saludo);

}

public void verBusqueda(String name)

{

op.busqueda(name);

}

public void establecerSendPending(boolean estado)

{

sendPending = estado;

}

public boolean obtenerSendPending()

{

return sendPending;

}

public boolean enviar()

{

return op.send();

}

public void verAñadirBuddy(String name, String id)

{

buddy.añadirBuddy(name,id);

}

public void cambiarInitForm(StringItem si)

Código de la Aplicación

JXTA sobre J2ME - 110 -

{

if (initForm.size() >= DEFAULT_SCROLL) {

initForm.delete(0);

}

initForm.append(si);

}

public void verSendForm()

{

op.sendForm();

}

public byte[] verObtenerState ()

{

return config.obtenerState();

}

public void verEstablecerState(byte[] state)

{

config.establecerState(state);

}

public boolean sondear()

{

return op.poll();

}

}

Código de la Aplicación

JXTA sobre J2ME - 111 -

Código de la Aplicación

JXTA sobre J2ME - 112 -

/****************************************************************************

* La clase Operaciones es donde se realizan las distintas operaciones *

* necesarias para la aplicación tales como conectarse, enviar mensajes, *

* sondear el relay, etc. Implementa CommandListener para detectar las *

* opciones elegidas en las distintas pantallas que contiene la clase. *

* *

****************************************************************************/

package net.jxta.midp.demo.michat;

import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.AlertType;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.TextField;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.StringItem;

import net.jxta.j2me.PeerNetwork;

import net.jxta.j2me.Message;

import net.jxta.j2me.Element;

public class Operaciones implements CommandListener

{

private static final int DEFAULT_ALERT_TIMEOUT = 5000;

private static final String INSTANTP2P_PIPEID = "urn:jxta:uuid-" +

"59616261646162614E50472050325033" +

"D1D1D1D1D1D141D191D191D1D1D1D1D104";

private static final boolean QUANTIFY = true;

private static final boolean DEBUG = true;

private static final String TALKNAME_PREFIX = "JxtaTalkUserName.";

private static final String INSTANTP2P_GROUPNAME = "IP2PGRP";

private Michat midlet;

private Form replyForm = null;

private Form confirmForm = null;

private TextField tfSentMsg = null;

private PeerNetwork peer = null;

private int responseId = -1;

private String selectedPipe = null;

protected boolean connected = false;

protected boolean connectInitiated = false;

public Operaciones(Michat midlet)

{

this.midlet = midlet;

}

public void sendForm() {

Código de la Aplicación

JXTA sobre J2ME - 113 -

if (replyForm == null) {

replyForm = new Form(midlet.obtenerCurrentBuddy());

tfSentMsg = new TextField(null,

null,

4096,

TextField.ANY);

replyForm.append(tfSentMsg);

Command cmdSend = new Command("Enviar", Command.SCREEN, 1);

Command cmdBack = new Command("Volver", Command.BACK, 2);

replyForm.addCommand(cmdSend);

replyForm.addCommand(cmdBack);

replyForm.setCommandListener(this) ;

}

replyForm.setTitle(midlet.obtenerCurrentBuddy());

midlet.display.setCurrent(replyForm);

}

public void setupConfirmForm() {

confirmForm = new Form("Restablecer configuracion");

Command cmdOK = new Command("OK", Command.OK, 1);

Command cmdBack = new Command("Volver", Command.BACK, 2);

StringItem si = new StringItem(null,

"Restablecer configuracion y lista de contactos por defecto, continuar?");

confirmForm.addCommand(cmdOK);

confirmForm.addCommand(cmdBack);

confirmForm.setCommandListener(this);

confirmForm.append(si);

}

public boolean send() {

if (peer == null || !connected) {

initiateConnect();

midlet.establecerSendPending(true);

return false;

}

String msg = tfSentMsg.getString();

Element[] elm = new Element[3];

elm[0] = new Element("JxtaTalkSenderName",

midlet.obtenerIdent().getBytes(),

null, null);

elm[1] = new Element("JxtaTalkSenderMessage",

msg.getBytes(),

null, null);

elm[2] = new Element("GrpName",

"NetPeerGroup".getBytes(),

null, null);

Message m = new Message(elm);

try {

String pipeId = midlet.obtenerCurrentBuddyId();

peer.send(pipeId, m);

} catch (Exception ex) {

Alert alert = new Alert("Enviar",

Código de la Aplicación

JXTA sobre J2ME - 114 -

"Error enviando mensaje: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

return false;

}

return true;

}

public void sendHello(String saludo){

Element[] elm = new Element[3];

elm[0] = new Element("JxtaTalkSenderName",

midlet.obtenerIdent().getBytes(),

null, null);

elm[1] = new Element("JxtaTalkSenderMessage",

saludo.getBytes(),

null, null);

elm[2] = new Element("GrpName",

"NetPeerGroup".getBytes(),

null, null);

Message m = new Message(elm);

try {

peer.send(INSTANTP2P_PIPEID, m);

midlet.dormirHilo();

}catch (Exception ex) {

Alert alert = new Alert("Enviar",

"Error enviando mensaje: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

}

public void initiateConnect() {

if (peer == null) {

peer = PeerNetwork.createInstance(midlet.obtenerIdent());

}

if (connected || connectInitiated) {

return;

}

connectInitiated = true;

// se continua la ejecucion en el hilo

}

public boolean connect() throws Exception {

connectInitiated = false;

String host = midlet.obtenerHost();

int port = 0;

try {

port = Integer.parseInt(midlet.obtenerPort());

} catch (NumberFormatException ex) {

Alert alert = new Alert("Conectar",

"Error obteniendo el puerto del relay: " +

midlet.obtenerPort(),

Código de la Aplicación

JXTA sobre J2ME - 115 -

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

return false;

}

String url = "http://" + host + ":" + Integer.toString(port);

if (DEBUG) {

System.out.println("Conectandose a " + url + "...");

}

long startTime, endTime;

if (QUANTIFY) {

startTime = System.currentTimeMillis();

}

byte[] state = midlet.verObtenerState();

state = peer.connect(url, state);

midlet.verEstablecerState(state);

if (QUANTIFY) {

endTime = System.currentTimeMillis();

System.out.println("conexion llevo " +

Long.toString(endTime-startTime));

}

connected = true;

String ip2pIdentity = TALKNAME_PREFIX + INSTANTP2P_GROUPNAME;

String miChatIdentity = TALKNAME_PREFIX + midlet.obtenerIdent();

if (DEBUG) {

System.out.println("creando tuberia ");

}

boolean ip2p = false;

boolean miPipe = false;

int idIP2PPipe = peer.create(PeerNetwork.PIPE,

ip2pIdentity,INSTANTP2P_PIPEID, "JxtaPropagate");

int idMiPipe = peer.create(PeerNetwork.PIPE,

miChatIdentity,selectedPipe, "JxtaUnicast");

System.out.println("ids de las tuberias "+idIP2PPipe+"\n"+idMiPipe);

while (!(ip2p&&miPipe))

{

poll();

System.out.println("esto es"+responseId);

if (responseId == idIP2PPipe)

ip2p = true;

if (responseId == idMiPipe)

miPipe = true;

if (midlet.stopPolling) {

System.out.println("salimos del bucle");

return false; }

try {

midlet.dormirHilo();

System.out.println("dormido");

} catch (Throwable t) {}

}

if (DEBUG) {

Código de la Aplicación

JXTA sobre J2ME - 116 -

System.out.println("Escuchando en " +

INSTANTP2P_PIPEID+"\n"+selectedPipe);

}

peer.listen(INSTANTP2P_PIPEID);

peer.listen(selectedPipe);

midlet.verInitForm();

return true;

}

public void disconnect() {

//cierro la tuberia y me desconecto

try {

peer.close(INSTANTP2P_PIPEID);

peer.close(selectedPipe);

connected = false;

if (DEBUG) {

System.out.println("Cerrada la tuberia");

}

} catch (Exception ex) {

Alert alert = new Alert("Desconectando",

"Error conectando con el relay: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(Alert.FOREVER);

midlet.verAlertInitForm(alert);

}

}

public boolean poll() {

if (peer == null || !connected) {

// no estoy conectado aun

return false;

}

Message msg = null;

try {

long startTime, endTime;

if (QUANTIFY) {

startTime = System.currentTimeMillis();

}

if (peer != null) {

msg = peer.poll(midlet.obtenerPollInterval()*1000);

}

if (QUANTIFY) {

endTime = System.currentTimeMillis();

System.out.println("sondeo llevo " +

Long.toString(endTime-startTime));

}

} catch (Exception ex) {

//ex.printStackTrace();

Alert alert = new Alert("Sondeo",

"Error sondeando el relay: " + ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

Código de la Aplicación

JXTA sobre J2ME - 117 -

return false;

}

if (msg == null) {

return false;

}

Element el = null;

String name = null;

String id = null;

for (int i=0; i < msg.getElementCount(); i++) {

el = msg.getElement(i);

if (Message.PROXY_NAME_SPACE.equals(el.getNameSpace())) {

String elementName = el.getName();

if (Message.NAME_TAG.equals(elementName)) {

name = new String(el.getData());

}else if (Message.ID_TAG.equals(elementName)) {

id = new String(el.getData());

}

}

}

if (name != null && id != null && !"".equals(id)) {

if (name.indexOf(TALKNAME_PREFIX) >= 0) {

name = name.substring(TALKNAME_PREFIX.length());

}

midlet.verAñadirBuddy(name,id);

}

String sender = null;

String message = null;

for (int i=0; i < msg.getElementCount(); i++) {

el = msg.getElement(i);

if ("requestId".equals(el.getName())) {

responseId = Integer.parseInt(new String(el.getData()));

}else if ("JxtaTalkSenderName".equals(el.getName())) {

sender = new String(el.getData());

} else if ("JxtaTalkSenderMessage".equals(el.getName())) {

message = new String(el.getData());

}

if (DEBUG) {

System.out.print(i + " " +el.getNameSpace() +" " +

el.getName());

System.out.print(" " + new String(el.getData()));

System.out.println();

}

}

if (sender != null && message != null) {

if (sender.indexOf(TALKNAME_PREFIX) >= 0) {

sender = sender.substring(TALKNAME_PREFIX.length());

}

Código de la Aplicación

JXTA sobre J2ME - 118 -

String displayedMsg = null;

if (id.equals(INSTANTP2P_PIPEID))

displayedMsg = sender + "@" + INSTANTP2P_GROUPNAME + "> " +

message + "\n";

else if(id.equals(selectedPipe))

displayedMsg = sender + ">" + message + "\n";

StringItem si = new StringItem(null, displayedMsg);

midlet.cambiarInitForm(si);

Alert alert = new Alert("","",

null, AlertType.INFO);

alert.setTimeout(1);

midlet.verAlertInitForm(alert);

}

return true;

}

public void commandAction(Command c, Displayable d) {

String label = c.getLabel();

if (d == replyForm) {

if (label.equals("Enviar")) {

if (send()) {

midlet.verInitForm();

}

} else if (label.equals("Volver")) {

midlet.verInitForm();

}

} else if (d == confirmForm) {

if (c.getCommandType() == Command.OK) {

midlet.resetConfig();

} else {

midlet.verInitForm();

}

}

}

public void verConfirmForm()

{

midlet.display.setCurrent(confirmForm);

}

public void ponerPeerANull()

{

peer = null;

}

public void busqueda(String name)

{

try

{

if (name == null)

peer.search(PeerNetwork.PIPE, "Name", TALKNAME_PREFIX + "*", 1);

else

peer.search(PeerNetwork.PIPE,"Name",

TALKNAME_PREFIX + name, 1);}

catch(Exception e){}

Código de la Aplicación

JXTA sobre J2ME - 119 -

}

public void establecerSelectedPipe(String pipe)

{

selectedPipe = pipe;

}

}

Código de la Aplicación

JXTA sobre J2ME - 120 -

/****************************************************************************

* En la clase Buddy se encuentran los métodos que permiten operaciones con *

* los contactos tales como recuperarlos y guardarlos en el RMS y añadir y *

* borrarlos a la lista de contactos. Implementa CommandListener para *

* detectar los eventos asociados a la selección de opciones en la pantalla *

* asociada a esta clase. *

* *

****************************************************************************/

package net.jxta.midp.demo.michat;

import javax.microedition.lcdui.List;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.TextField;

import javax.microedition.lcdui.Displayable;

import javax.microedition.rms.RecordStore;

import javax.microedition.rms.RecordStoreException;

import java.util.Hashtable;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

public class Buddy implements CommandListener

{

private static final String RECORD_STORE_NAME = "jxta-midp-chat";

private static final int BUDDYLIST_RECORD_INDEX = 2;

private static final int MAX_TEXTFIELD_SIZE = 256;

private static final boolean DEBUG = true;

private List buddyList = null;

private Form addBuddyForm = null;

private Michat midlet;

private Hashtable buddyIds;

private TextField tfBuddy = null;

public Buddy(Michat midlet)

{

this.midlet = midlet;

buddyIds = new Hashtable();

}

public void editBuddies() {

midlet.display.setCurrent(buddyList);

}

public void readBuddies() {

buddyList = new List("Lista de contactos", List.IMPLICIT);

Command cmdChat = new Command("Enviar privado", Command.SCREEN, 1);

Command cmdAdd = new Command("Añadir", Command.SCREEN, 2);

Command cmdDelete = new Command("Borrar", Command.SCREEN, 3);

Código de la Aplicación

JXTA sobre J2ME - 121 -

Command cmdSearch = new Command("Buscar nuevos contactos",

Command.SCREEN, 4);

Command cmdBack = new Command("Volver", Command.BACK, 5);

buddyList.addCommand(cmdChat);

buddyList.addCommand(cmdAdd);

buddyList.addCommand(cmdDelete);

buddyList.addCommand(cmdSearch);

buddyList.addCommand(cmdBack);

buddyList.setCommandListener(this) ;

RecordStore rs = null;

try {

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, false);

byte[] data = rs.getRecord(BUDDYLIST_RECORD_INDEX);

ByteArrayInputStream bais = new ByteArrayInputStream(data);

DataInputStream dis = new DataInputStream(bais);

int size = dis.readShort();

if (DEBUG) {

System.out.println("Leyendo contactos: numero=" + size);

}

for (int i=0; i < size; i++) {

String buddy = dis.readUTF();

buddyList.append(buddy, null);

String buddyId = dis.readUTF();

buddyIds.put(buddy, buddyId);

if (DEBUG) {

System.out.println(" Contacto leido =\"" + buddy +

"\" id=\"" + buddyId + "\"");

}

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

}

}

}

public void storeBuddies() {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

DataOutputStream dos = new DataOutputStream(baos);

RecordStore rs = null;

try {

int size = buddyList.size();

Código de la Aplicación

JXTA sobre J2ME - 122 -

if (DEBUG) {

System.out.println("Salvando contactos: numero=" + size);

}

dos.writeShort(size);

for (int i=0; i < size; i++) {

String buddy = buddyList.getString(i);

dos.writeUTF(buddy);

String buddyId = (String) buddyIds.get(buddy);

if (buddyId == null) {

// not yet discovered

buddyId = "";

}

dos.writeUTF(buddyId);

if (DEBUG) {

System.out.println(" Contacto salvado=\"" +

buddy + "\" id=\"" + buddyId + "\"");

}

}

dos.close();

byte[] data = baos.toByteArray();

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, true);

int recordId = BUDDYLIST_RECORD_INDEX;

try {

rs.getRecord(recordId);

rs.setRecord(recordId, data, 0, baos.size());

} catch (RecordStoreException rex) {

recordId = rs.addRecord(data, 0, baos.size());

}

if (DEBUG) {

System.out.println("Salvada lista de contactos: recordId=" +

recordId +

" numero=" + size +

" dlen=" + data.length);

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

dos.close();

baos.close();

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

}

}

}

public void chatBuddy() {

Código de la Aplicación

JXTA sobre J2ME - 123 -

int i = buddyList.getSelectedIndex();

String buddy = buddyList.getString(i);

midlet.cambiarCurrentBuddy(buddy);

midlet.verSendForm();

}

public void addBuddy() {

addBuddyForm = new Form("Añadir contacto");

tfBuddy = new TextField("Nuevo contacto: ",

"",

MAX_TEXTFIELD_SIZE,

TextField.ANY);

addBuddyForm.append(tfBuddy);

Command cmdOK = new Command("OK", Command.OK, 1);

addBuddyForm.addCommand(cmdOK);

addBuddyForm.setCommandListener(this) ;

midlet.display.setCurrent(addBuddyForm);

}

public void deleteBuddy() {

int i = buddyList.getSelectedIndex();

buddyList.delete(i);

}

public void commandAction(Command c, Displayable d) {

String label = c.getLabel();

if (d == buddyList) {

if (c.getCommandType() == Command.BACK) {

midlet.verInitForm();

storeBuddies();

}

if (label.equals("Enviar privado")) {

chatBuddy();

storeBuddies();

} else if (label.equals("Añadir")) {

addBuddy();

} else if (label.equals("Borrar")) {

deleteBuddy();

storeBuddies();

} else if (label.equals("Buscar nuevos contactos")) {

try {

midlet.verBusqueda(null);}

catch (Exception e) {}

midlet.verInitForm();

}

} else if (d == addBuddyForm) {

if (c.getCommandType() == Command.OK) {

String buddyAdded = tfBuddy.getString();

try {

midlet.verBusqueda(buddyAdded);}

catch (Exception e){}

midlet.verInitForm();

}

Código de la Aplicación

JXTA sobre J2ME - 124 -

}

}

public String obtenerBuddyId(String buddy)

{

String buddyId = (String) buddyIds.get(buddy);

return buddyId;

}

public void añadirBuddy(String name, String id)

{

boolean flag = false;

int size = buddyList.size();

for (int i=0; i < size; i++) {

String buddy = buddyList.getString(i);

if (buddy.equals(name)) {

flag = true;

break;

}

}

if (!flag) {

buddyList.append(name,null);

storeBuddies();

}

buddyIds.put(name, id);

}

}

Código de la Aplicación

JXTA sobre J2ME - 125 -

/****************************************************************************

* La clase Conf será la encargada de almacenar y recuperar la información *

* sobre la configuración de la aplicación. Para ello utilizaremos el *

* mecanismo de almacenamiento permanente (RMS) del que disponen los *

* dispositivos MID. Implementa un CommandListener para detectar las opciones*

* elegidas en el formulario que se crea en la clase. *

* *

****************************************************************************/

package net.jxta.midp.demo.michat;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.TextField;

import javax.microedition.lcdui.ChoiceGroup;

import javax.microedition.lcdui.Choice;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.AlertType;

import javax.microedition.rms.RecordStore;

import javax.microedition.rms.RecordStoreException;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

public class Conf implements CommandListener

{

private static final String RECORD_STORE_NAME = "jxta-midp-chat";

private static final int CONFIG_RECORD_INDEX = 1;

private static final int DEFAULT_ALERT_TIMEOUT = 5000;

private static final int MAX_TEXTFIELD_SIZE = 256;

private static final String[] PIPE_IDS_CHOICES = { "PIPE 1", "PIPE 2",

"PIPE 3" };

private static final String MI_PIPEID1 = "urn:jxta:uuid-" +

"59616261646162614E50472050325033E" +

"574D38B19784F39AC91EA77E162A10604";

private static final String MI_PIPEID2 = "urn:jxta:uuid-" +

"59616261646162614E50472050325033E" +

"3CE324961BC4FD28252F82CF4BA709F04";

private static final String MI_PIPEID3 = "urn:jxta:uuid-" +

"59616261646162614E504720503250338" +

"2F310EA59804AB3880B816B06A1F4A704";

private static final boolean DEBUG = true;

private Michat midlet;

private Form configForm = null;

private TextField tfRelayHost = null;

private TextField tfRelayPort = null;

private TextField tfIdentity = null;

private TextField tfPollInterval = null;

private ChoiceGroup cgPipeId = null;

Código de la Aplicación

JXTA sobre J2ME - 126 -

private byte[] state = new byte[0];

public Conf(Michat midlet)

{

this.midlet = midlet;

}

public void editConfig() {

if (configForm == null) {

configForm = new Form("Configuracion");

configForm.append(tfRelayHost);

configForm.append(tfRelayPort);

configForm.append(tfIdentity);

configForm.append(tfPollInterval);

configForm.append(cgPipeId);

cgPipeId.setSelectedIndex(0, true);

Command cmdOK = new Command("OK", Command.OK, 1);

Command cmdBack = new Command("Volver", Command.BACK, 2);

configForm.addCommand(cmdOK);

configForm.addCommand(cmdBack);

configForm.setCommandListener(this) ;

}

midlet.display.setCurrent(configForm);

}

public void readConfig() {

tfRelayHost = new TextField("Host Relay: ",

"192.18.37.36",

MAX_TEXTFIELD_SIZE,

TextField.ANY);

tfRelayPort = new TextField("Puerto Relay: ",

"9700",

MAX_TEXTFIELD_SIZE,

TextField.NUMERIC);

tfIdentity = new TextField("Nick: ",

"",

MAX_TEXTFIELD_SIZE,

TextField.ANY);

tfPollInterval = new TextField("Intervalo de sondeo: ",

Integer.toString(midlet.obtenerPollInterval()),

MAX_TEXTFIELD_SIZE,

TextField.NUMERIC);

cgPipeId = new ChoiceGroup("Ids de Pipes", Choice.EXCLUSIVE,

PIPE_IDS_CHOICES,

null);

cgPipeId.setSelectedIndex(0, true);

RecordStore rs = null;

Código de la Aplicación

JXTA sobre J2ME - 127 -

try {

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, false);

byte[] data = rs.getRecord(CONFIG_RECORD_INDEX);

ByteArrayInputStream bais = new ByteArrayInputStream(data);

DataInputStream dis = new DataInputStream(bais);

tfRelayHost.setString(dis.readUTF());

tfRelayPort.setString(dis.readUTF());

tfIdentity.setString(dis.readUTF());

tfPollInterval.setString(dis.readUTF());

try {

int interval = Integer.parseInt(tfPollInterval.getString());

midlet.establecerPollInterval(interval);

} catch (NumberFormatException ex) {

Alert alert = new Alert("Leyendo configuracion",

"Error al obtener el intervalo de sondeo: " +

tfPollInterval.getString(),null,

AlertType.WARNING);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

int stateLen = dis.readShort();

state = new byte[stateLen];

dis.readFully(state);

if (DEBUG) {

System.out.println("Leyendo configuracion: host=" +

tfRelayHost.getString() +

" puerto=" + tfRelayPort.getString() +

" nick=" + tfIdentity.getString() +

" intervalo=" + tfPollInterval.getString()

+

" stateLen=" + stateLen);

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

}

}

}

public void storeConfig() {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

DataOutputStream dos = new DataOutputStream(baos);

RecordStore rs = null;

Código de la Aplicación

JXTA sobre J2ME - 128 -

try {

dos.writeUTF(tfRelayHost.getString());

dos.writeUTF(tfRelayPort.getString());

dos.writeUTF(tfIdentity.getString());

dos.writeUTF(tfPollInterval.getString());

dos.writeShort(state.length);

dos.write(state);

dos.close();

byte[] data = baos.toByteArray();

rs = RecordStore.openRecordStore(RECORD_STORE_NAME, true);

int recordId = CONFIG_RECORD_INDEX;

try {

rs.getRecord(recordId);

rs.setRecord(recordId, data, 0, baos.size());

} catch (RecordStoreException rex) {

recordId = rs.addRecord(data, 0, baos.size());

}

if (DEBUG) {

System.out.println("Configuracion salvada: recordId=" +

recordId +

" host=" + tfRelayHost.getString() +

" puerto=" + tfRelayPort.getString() +

" nick=" + tfIdentity.getString() +

" intervalo=" + tfPollInterval.getString()

+

" stateLen=" + state.length);

}

try {

int interval = Integer.parseInt(tfPollInterval.getString());

midlet.establecerPollInterval(interval);

} catch (NumberFormatException ex) {

Alert alert = new Alert("Configuracion salvada",

"Error obteniendo intervalo de sondeo: " +

tfPollInterval.getString(),null,

AlertType.WARNING);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

}

} finally {

try {

if (rs != null) {

rs.closeRecordStore();

}

dos.close();

baos.close();

} catch (Exception ex) {

if (DEBUG) {

System.out.println(ex);

Código de la Aplicación

JXTA sobre J2ME - 129 -

}

}

}

}

public void commandAction(Command c, Displayable d) {

String label = c.getLabel();

if (d == configForm) {

if (c.getCommandType() == Command.OK) {

int i = cgPipeId.getSelectedIndex();

String selPipe = cgPipeId.getString(i);

if (selPipe.equals("PIPE 1"))

midlet.verEstablecerSelectedPipe(MI_PIPEID1);

else if (selPipe.equals("PIPE 2"))

midlet.verEstablecerSelectedPipe(MI_PIPEID2);

else if (selPipe.equals("PIPE 3"))

midlet.verEstablecerSelectedPipe(MI_PIPEID3);

storeConfig();

midlet.verInitForm();

} else if (c.getCommandType() == Command.BACK) {

configForm = null;

readConfig();

midlet.verInitForm();

}

}

}

public String obtenerIdentidad()

{

return tfIdentity.getString();

}

public String obtenerDirHost()

{

return tfRelayHost.getString();

}

public String obtenerNumPort()

{

return tfRelayPort.getString();

}

public byte[] obtenerState()

{

return state;

}

public void establecerState(byte[] estado)

{

state = estado;

}

public void ponerConfigFormNull()

{

configForm = null;

}

}

Código de la Aplicación

JXTA sobre J2ME - 130 -

/****************************************************************************

* La clase PollThread implementa el hilo que se ejecutará en segundo plano *

* para realizar la operación periódica de sondeo del relay. Dentro de la *

* clase, también se efectuará la llamada al método connect de la clase *

* Operaciones a través de la clase Michat y se enviará el mensaje de *

* conexión al NetPeerGroup a través de la tubería del IP2P Group. *

* *

****************************************************************************/

package net.jxta.midp.demo.michat;

import java.util.Timer;

import java.util.TimerTask;

import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.AlertType;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.StringItem;

import javax.microedition.lcdui.Gauge;

public class PollThread extends Thread

{

private static final boolean DEBUG = true;

private static final int DEFAULT_ALERT_TIMEOUT = 5000;

private Michat midlet;

private Form connectingForm = null;

public PollThread(Michat midlet)

{

this.midlet = midlet;

start();

}

public void run() {

if (DEBUG) {

System.out.println("iniciando el hilo de sondeo");

}

while (!midlet.stopPolling) {

if (midlet.verCondConnect()) {

Form connectingForm = new Form("Conectando...");

StringItem elapsed = new StringItem("", null);

connectingForm.append(elapsed);

Gauge status = new Gauge(null, false, 10, 0);

connectingForm.append(status);

midlet.display.setCurrent(connectingForm);

StatusUpdate updater = new StatusUpdate(elapsed, status);

new Timer().scheduleAtFixedRate(updater, 1000, 1000);

try {

midlet.conectar();

updater.cancel();

midlet.verInitForm();

connectingForm = null;

} catch (Exception ex) {

updater.cancel();

Alert alert = new Alert("Conectando",

Código de la Aplicación

JXTA sobre J2ME - 131 -

"Error conectando al relay: " +

ex.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(Alert.FOREVER);

midlet.verAlertInitForm(alert);

}

//envio los mensajes pendientes

try {

String saludo = " has joined NetPeerGroup\n";

midlet.saludar(saludo);

midlet.verBusqueda(null);

}catch (Exception e ) {}

if (midlet.obtenerSendPending()) {

try {

midlet.enviar();

} finally {

midlet.establecerSendPending(false);

}

}

}

try {

// mientras existan mensajes, sigo sondeando el relay

while (midlet.sondear()) { }

}catch (Throwable t) {

Alert alert = new Alert("Sondeando",

"Error procesando el mensaje: " + t.getMessage(),

null, AlertType.ERROR);

alert.setTimeout(DEFAULT_ALERT_TIMEOUT);

midlet.verAlertInitForm(alert);

}

midlet.dormirHilo();

}

if (DEBUG) {

System.out.println("paro el hilo");

}

}

}

Código de la Aplicación

JXTA sobre J2ME - 132 -

/****************************************************************************

* La clase StatusUpdate se encarga de crear una barra de progreso que irá *

* incrementándose tras la solicitud de conexión y hasta que ésta se haya *

* completado. Para ello, StatusUpdate extiende a la clase TimerTask y se *

* utiliza para crear un objeto Timer en la clase PollThread, que producirá *

* un evento cada segundo. Cada vez que se capture este evento, se ejecutará *

* el método run de StatusUpdate. *

* *

****************************************************************************/

package net.jxta.midp.demo.michat;

import javax.microedition.lcdui.StringItem;

import javax.microedition.lcdui.Gauge;

import java.util.Timer;

import java.util.TimerTask;

// clase para incrementar el Gauge al conectarnos

public class StatusUpdate extends TimerTask {

private StringItem elapsed = null;

private Gauge status = null;

private int tick = 0;

private int max = 0;

StatusUpdate(StringItem elapsed, Gauge status) {

this.elapsed = elapsed;

this.status = status;

max = status.getMaxValue();

}

public void run() {

elapsed.setText(Integer.toString(++tick) + "s");

status.setValue(tick % max);

}

}

Código de la Aplicación

JXTA sobre J2ME - 133 -

/************************************************************************

* Clase PeerNetwork:

*

* $Id: PeerNetwork.java,v 1.31 2003/09/11 06:46:28 kuldeep Exp $

*

* Copyright (c) 2001 Sun Microsystems, Inc. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions

* are met:

*

* 1. Redistributions of source code must retain the above copyright

* notice, this list of conditions and the following disclaimer.

*

* 2. Redistributions in binary form must reproduce the above copyright

* notice, this list of conditions and the following disclaimer in

* the documentation and/or other materials provided with the

* distribution.

*

* 3. The end-user documentation included with the redistribution,

* if any, must include the following acknowledgment:

* "This product includes software developed by the

* Sun Microsystems, Inc. for Project JXTA."

* Alternately, this acknowledgment may appear in the software itself,

* if and wherever such third-party acknowledgments normally appear.

*

* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"

* must not be used to endorse or promote products derived from this

* software without prior written permission. For written

* permission, please contact Project JXTA at http://www.jxta.org.

*

* 5. Products derived from this software may not be called "JXTA",

* nor may "JXTA" appear in their name, without prior written

* permission of Sun.

*

* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED

* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR

* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF

* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,

* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT

* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

* SUCH DAMAGE.

*

* ====================================================================

*

* This software consists of voluntary contributions made by many

* individuals on behalf of Project JXTA. For more

* information on Project JXTA, please see

* <http://www.jxta.org/>.

*

* This license is based on the BSD license adopted by the Apache

Código de la Aplicación

JXTA sobre J2ME - 134 -

* Foundation.

**********************************************************************/

package net.jxta.j2me;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.util.Vector;

/**

* This class is an abstraction for the JXTA Network. It specifies the

* operations that an application can invoke on the JXTA network.

*/

public final class PeerNetwork {

/**

* The group, when not specified, defaults to the

* <code>NetPeerGroup</code>.

*/

public static final String DEFAULT_GROUP = "urn:jxta:jxta-NetGroup";

/**

* The JXTA Unicast pipe. Use for one-to-one communications with a

* peer.

*/

public static final String UNICAST_PIPE = "JxtaUnicast";

/**

* The JXTA Propagte pipe. Messages sent to this pipe are

* propagated to the entire group.

*/

public static final String PROPAGATE_PIPE = "JxtaPropagate";

/**

* Create or Search for a Peer.

*/

public static final String PEER = "PEER";

/**

* Create or Search for a Group.

*/

public static final String GROUP = "GROUP";

/**

* Create or Search for a Pipe.

*/

public static final String PIPE = "PIPE";

/**

* Search for other resources

*/

public static final String ADV = "ADV";

private static final String PROXY_SERVICE_NAME =

Código de la Aplicación

JXTA sobre J2ME - 135 -

"urn:jxta:uuid-DEADBEEFDEAFBABAFEEDBABE0000000E05";

private Vector sendMessageQueue = new Vector();

/**

* The default constructor is private. Use the createInstance

* factory method instead.

*/

private PeerNetwork() {

}

/**

* Connect to a relay. A connection to a relay needs to be

* established before any of the other operations can be invoked.

*

* @param url relay URL

*

* @param state a byte array that represents the persistent state

* of a connection to the PeerNetwork. Initially, this would be

* null. A successful {@link #connect} returns this state

* information which the application is expected to persist and

* pass it back to connect, if available.

*

* @return see the description for the state parameter

*

* @throws IOException if the relay is not accesible

*/

public byte[] connect(String url, byte[] state)

throws IOException {

relayUrl = url;

String persistedPeerId = null;

if (state != null && state.length > 0) {

persistedPeerId = new String(state);

}

peerId = messenger.connect(url, persistedPeerId);

if (peerId == null) {

throw new IOException("Connect did not return a peerId");

}

return peerId.getBytes();

}

/**

* Ask the proxy on the relay to join a new group so that the

* application can then create a new instance of the PeerNetwork

* which would be a member of the new group. Note that the

* application must wait for a success response from the proxy for

* the join request and only then attempt to create another

* instance of the PeerNetwork that would be a member of the new

* group.

*

* <p>

*

* The proxy can be asked to join multiple groups by issuing this

Código de la Aplicación

JXTA sobre J2ME - 136 -

* request multiple times. Currently there is no leave command,

* but the proxy could decide to leave the group if there are no

* more active clients using that group.

*

* @param newGroupId the id of the group to join. This id can be

* obtained by invoking the {@link #search} command for the group

* and waiting for the response.

*

* @param password the password required to join the group, if one is

* required. Otherwise, it is ignored. (Note: currently it is always

* ignored. The proxy can only join null-membership groups.

*

* @return query id that can be used to match responses

*

* @throws IOException if a communication error occurs with the

* relay or with the JXTA network

*/

public int join(String newGroupId, String password)

throws IOException {

if (password == null) {

password = "";

}

int requestId = getNextRequestId();

Element[] elm = new Element[4];

elm[0] = new Element(Message.REQUEST_TAG,

Message.REQUEST_JOIN.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[1] = new Element(Message.ID_TAG,

newGroupId.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[2] = new Element(Message.ARG_TAG,

password.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[3] = new Element(Message.REQUESTID_TAG,

Integer.toString(requestId).getBytes(),

Message.PROXY_NAME_SPACE, null);

sendMessage(elm);

return requestId;

}

/**

* Search for Peers, Groups or Pipes.

*

* @param type one of

* {@link #PEER}, {@link #GROUP} or {@link #PIPE} or {@link #ADV}

*

* @param attr the name of the attribute to search

* for. This is one of the fields that Advertisements

* are indexed on, returned by

* {@link

http://platform.jxta.org/nonav/java/api/net/jxta/document/Advertisement.html#

getIndexFields()}

* in the JXTA J2SE API. The String <code>Name</code>

* is usually used to search advertisements by name,

* for example.

Código de la Aplicación

JXTA sobre J2ME - 137 -

*

* @param query an expression specifying the items being searched

* for and also limiting the scope of items to be returned. This

* is usually a simple regular expression such as, for example,

* <code>TicTacToe*</code> to search for all entities with names

* that begin with TicTacToe.

*

* @param threshold the maximum number of responses

* allowed from any one peer

*

* @return query id that can be used to match responses

*

* @throws IOException if a communication error occurs with the

* relay or with the JXTA network

*/

public int search(String type, String attr, String query, int threshold)

throws IOException {

int requestId = getNextRequestId();

Element[] elm = new Element[6];

elm[0] = new Element(Message.REQUEST_TAG,

Message.REQUEST_SEARCH.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[1] = new Element(Message.TYPE_TAG,

type.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[2] = new Element(Message.ATTRIBUTE_TAG,

attr.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[3] = new Element(Message.VALUE_TAG,

query.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[4] = new Element(Message.THRESHOLD_TAG,

Integer.toString(threshold).getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[5] = new Element(Message.REQUESTID_TAG,

Integer.toString(requestId).getBytes(),

Message.PROXY_NAME_SPACE, null);

sendMessage(elm);

return requestId;

}

/**

* Create a

* {@link #PEER} {@link #GROUP} or {@link #PIPE}

*

* @param type one of

* {@link #PEER}, {@link #GROUP} or {@link #PIPE}

*

* @param name the name of the entity being created.

*

* @param id pre-defined id of the entity bieng created. Can be null

*

* @param arg an optional arg depending upon the type of entity

* being created. For example, for {@link #PIPE}, this would be

* the type of {@link #PIPE} that is to be created. For example,

* <code>JxtaUniCast</code> and <code>JxtaPropagate</code> are

Código de la Aplicación

JXTA sobre J2ME - 138 -

* commonly-used values. This parameter can be <code>null</code>.

*

* @return query id that can be used to match responses.

*

* @throws IOException if a communication error occurs with the

* relay or with the JXTA network

*/

public int create(String type, String name, String id, String arg)

throws IOException {

int requestId = getNextRequestId();

int numberElements = 4;

if (id != null) {

numberElements++;

}

if (arg != null) {

numberElements++;

}

Element[] elm = new Element[numberElements];

elm[0] = new Element(Message.REQUEST_TAG,

Message.REQUEST_CREATE.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[1] = new Element(Message.NAME_TAG,

name.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[2] = new Element(Message.REQUESTID_TAG,

Integer.toString(requestId).getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[3] = new Element(Message.TYPE_TAG,

type.getBytes(),

Message.PROXY_NAME_SPACE, null);

int index = 4;

if (id != null) {

elm[index++] = new Element(Message.ID_TAG,

id.getBytes(),

Message.PROXY_NAME_SPACE, null);

}

if (arg != null) {

elm[index] = new Element(Message.ARG_TAG,

arg.getBytes(),

Message.PROXY_NAME_SPACE, null);

}

sendMessage(elm);

return requestId;

}

/**

* Open a Pipe for input.

*

* @param id the id of the Pipe.

Código de la Aplicación

JXTA sobre J2ME - 139 -

*

* @return query id that can be used to match responses

*

* @throws IOException if a communication error occurs with the

* relay or with the JXTA network

*

* @throws IllegalArgumentException if id is null

*/

public int listen(String id)

throws IOException, IllegalArgumentException {

return pipeOperation(Message.REQUEST_LISTEN, id, null);

}

/**

* Close an input Pipe.

*

* @param id the id of the Pipe.

*

* @return query id that can be used to match responses

*

* @throws IOException if a communication error occurs with the

* relay or with the JXTA network

*

* @throws IllegalArgumentException if id is null

*/

public int close(String id)

throws IOException, IllegalArgumentException {

return pipeOperation(Message.REQUEST_CLOSE, id, null);

}

/**

* Send data to the specified Pipe.

*

* @param id the peer or pipe id to which data is to be sent.

*

* @param data a {@link Message} containing an array of

* {@link Element}s which contain application data that is to

* be sent

*

* @return query id that can be used to match responses, if any

*

* @throws IOException if there is a problem in sending

*

* @throws IllegalArgumentException if id is null

*/

public int send(String id, Message data)

throws IOException, IllegalArgumentException {

return pipeOperation(Message.REQUEST_SEND, id, data);

}

private int pipeOperation(String op, String id, Message data)

throws IOException, IllegalArgumentException {

if (id == null || id.length() == 0) {

Código de la Aplicación

JXTA sobre J2ME - 140 -

throw new IllegalArgumentException ("Pipe ID not specified");

}

int requestId = getNextRequestId();

int numberElements = 3;

if (data != null) {

numberElements += data.getElementCount();

}

Element[] elm = new Element[numberElements];

elm[0] = new Element(Message.REQUEST_TAG,

op.getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[1] = new Element(Message.REQUESTID_TAG,

Integer.toString(requestId).getBytes(),

Message.PROXY_NAME_SPACE, null);

elm[2] = new Element(Message.ID_TAG,

id.getBytes(),

Message.PROXY_NAME_SPACE, null);

int index = 3;

if (data != null) {

for (int i=0; i < data.getElementCount(); i++) {

elm[index++] = data.getElement(i);

}

}

sendMessage(elm);

return requestId;

}

/**

* Poll the relay for messages addressed to this Peer.

*

* <p>For optimum performance, it is <em>highly</em> recommended

* that this method be called repeatedly until it returns

* <code>null</code>, draining all queued messages before sending

* out any new messages.</p>

*

* @param int timeout time in milliseconds to wait for the

* response. A timeout of <code>0</code> means wait forever.

*

* @return a {@link Message} containing an array of

* {@link Element}s containing incoming data. Will return a

* <code>null</code> if there are no incoming {@link Message}s.

*

* @throws IOException if there is a problem in communicating with

* the relay

*/

public Message poll(int timeout)

throws IOException {

Código de la Aplicación

JXTA sobre J2ME - 141 -

/* Send an empty message when there are no messages to

send. This helps maintain persistent connections to the

relay */

Message outgoing = Message.EMPTY;

// if there is a queued message, send it first

if (!sendMessageQueue.isEmpty()) {

outgoing = (Message) sendMessageQueue.elementAt(0);

sendMessageQueue.removeElementAt(0);

}

// this is a good time to GC... we will be stuck for a while

// on IO and we may need room if we have a large incoming

// message

System.gc();

return messenger.poll(timeout, outgoing);

}

/**

* Factory method, used to create an instance of a PeerNetwork.

*

* @param peername a name that the user would like to give to

* this Peer. It need not be unique, but it is better for MIDP

* clients if it is so.

*

* @return an instance of PeerNetwork.

*/

public static PeerNetwork createInstance(String peername) {

return new PeerNetwork(peername, DEFAULT_GROUP);

}

/**

* Factory method, used to create an instance of a PeerNetwork.

*

* @param peername a name that the user would like to give to

* this Peer. It need not be unique, but it is better for MIDP

* clients if it is so.

*

* @param groupId the id of the group to join.

*

* @return an instance of PeerNetwork.

*/

public static PeerNetwork createInstance(String peername,

String groupId) {

return new PeerNetwork(peername, groupId);

}

/*

* Implementation starts here

*/

private static int nextRequestId = -1;

Código de la Aplicación

JXTA sobre J2ME - 142 -

private String peername = null;

private String groupId = null;

private String trimmedGID = null;

private HttpMessenger messenger = null;

private String relayUrl = null;

private String peerId = null;

private PeerNetwork(String peername, String groupId) {

if (peername == null) {

throw new IllegalArgumentException("Peer name must be

specified");

}

if (groupId == null) {

throw new IllegalArgumentException("Group id must be specified");

}

this.peername = peername;

this.groupId = groupId;

int ndx = groupId.indexOf ("urn:jxta:");

if (ndx != -1) {

trimmedGID = groupId.substring (ndx + "urn:jxta:".length());

} else {

trimmedGID = groupId;

}

messenger = new HttpMessenger();

}

private void sendMessage(Element[] elm)

throws IOException {

if (peerId == null) {

throw new IOException("Must connect before sending a message");

}

String proxyAddr =

relayUrl + "/EndpointService:" + trimmedGID + "/" +

PROXY_SERVICE_NAME + "/" + groupId;

//System.out.println ("************" + proxyAddr );

Element destAddrElem =

new Element("EndpointDestinationAddress",

proxyAddr.getBytes(), "jxta", null);

String sourceAddr = "jxta://" + peerId;

Element srcAddrElem =

new Element("EndpointSourceAddress",

sourceAddr.getBytes(), "jxta", null);

// we will add two elements to the end of the message

Element[] elm2 = new Element[elm.length + 2];

for (int i=0; i < elm.length; i++) {

elm2[i] = elm[i];

}

// add the src and dest address elements

elm2[elm.length] = destAddrElem;

elm2[elm.length + 1] = srcAddrElem;

Código de la Aplicación

JXTA sobre J2ME - 143 -

Message msg = new Message(elm2);

sendMessageQueue.addElement(msg);

}

private static synchronized int getNextRequestId() {

if (++nextRequestId < 0) {

nextRequestId = 0;

}

return nextRequestId;

}

}

Código de la Aplicación

JXTA sobre J2ME - 144 -

/************************************************************************

* Clase Message:

*

* $Id: Message.java,v 1.12 2003/04/09 21:18:00 akhil Exp $

*

* Copyright (c) 2001 Sun Microsystems, Inc. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions

* are met:

*

* 1. Redistributions of source code must retain the above copyright

* notice, this list of conditions and the following disclaimer.

*

* 2. Redistributions in binary form must reproduce the above copyright

* notice, this list of conditions and the following disclaimer in

* the documentation and/or other materials provided with the

* distribution.

*

* 3. The end-user documentation included with the redistribution,

* if any, must include the following acknowledgment:

* "This product includes software developed by the

* Sun Microsystems, Inc. for Project JXTA."

* Alternately, this acknowledgment may appear in the software itself,

* if and wherever such third-party acknowledgments normally appear.

*

* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"

* must not be used to endorse or promote products derived from this

* software without prior written permission. For written

* permission, please contact Project JXTA at http://www.jxta.org.

*

* 5. Products derived from this software may not be called "JXTA",

* nor may "JXTA" appear in their name, without prior written

* permission of Sun.

*

* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED

* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR

* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF

* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,

* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT

* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

* SUCH DAMAGE.

*

* ====================================================================

*

* This software consists of voluntary contributions made by many

* individuals on behalf of Project JXTA. For more

* information on Project JXTA, please see

* <http://www.jxta.org/>.

*

* This license is based on the BSD license adopted by the Apache

Código de la Aplicación

JXTA sobre J2ME - 145 -

* Foundation.

**********************************************************************/

package net.jxta.j2me;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.util.Enumeration;

import java.util.Hashtable;

/**

* This class represents a JXTA Message. A JXTA Message is composed of

* several {@link net.jxta.j2me.Element}s. The Elements can be in any

* order, but certain elements are reserved for use by the JXTA

* Network. These private Elements use a private namespace.<p>

*

* It also defines convenience methods for accessing commonly-used

* properties for handling responses to the asynchronous operations

* defined in {@link net.jxta.j2me.PeerNetwork}.<p>

*

* This is an immutable class.

*/

public final class Message {

public static final String DEFAULT_NAME_SPACE = "";

public static final String JXTA_NAME_SPACE = "jxta";

public static final String PROXY_NAME_SPACE = "proxy";

public static final String DEFAULT_MIME_TYPE = "application/x-jxta-msg";

public static final String REQUESTID_TAG = "requestId";

public static final String TYPE_TAG = "type";

public static final String NAME_TAG = "name";

public static final String ID_TAG = "id";

public static final String ARG_TAG = "arg";

public static final String ATTRIBUTE_TAG = "attr";

public static final String VALUE_TAG = "value";

public static final String THRESHOLD_TAG = "threshold";

public static final String REQUEST_TAG = "request";

public static final String RESPONSE_TAG = "response";

public static final String ERROR_TAG = "error";

public static final String REQUEST_JOIN = "join";

public static final String REQUEST_CREATE = "create";

public static final String REQUEST_SEARCH = "search";

public static final String REQUEST_LISTEN = "listen";

public static final String REQUEST_CLOSE = "close";

public static final String REQUEST_SEND = "send";

public static final String RESPONSE_SUCCESS = "success";

public static final String RESPONSE_ERROR = "error";

public static final String RESPONSE_INFO = "info";

public static final String RESPONSE_RESULT = "result";

public static final String RESPONSE_MESSAGE = "data";

Código de la Aplicación

JXTA sobre J2ME - 146 -

private static final String JXTA_MESSAGE_HEADER = "jxmg";

private static final int MESSAGE_VERSION = 0;

private static Hashtable static_ns2id = new Hashtable(2);

private static Hashtable static_id2ns = new Hashtable(2);

private static final ByteCounterOutputStream

byteCounter = new ByteCounterOutputStream();

private static final DataOutputStream

dataCounter = new DataOutputStream(byteCounter);

/**

* An empty Message to send when we have no outgoing message. This

* helps maintain a persistent connection to an HTTP relay.

*/

public static final Message EMPTY = new Message();

private Element[] elements = null;

private int nextNameSpaceId = 0;

static {

int nextNameSpaceId = 0;

Integer id = new Integer(nextNameSpaceId++);

static_ns2id.put(DEFAULT_NAME_SPACE, id);

static_id2ns.put(id, DEFAULT_NAME_SPACE);

id = new Integer(nextNameSpaceId++);

static_ns2id.put(JXTA_NAME_SPACE, id);

static_id2ns.put(id, JXTA_NAME_SPACE);

}

/**

* Construct an empty Message, without any Elements.

*/

private Message() {

elements = new Element[0];

}

/**

* Construct a Message from an array of Elements. The supplied

* Elements are passed along as-is to the relay. Typically, these

* Elements would hold application data. Internally, JXTA for J2ME

* may add its own Elements to the Message for routing and other

* purposes.

*

* @param elms an array of elements

*/

public Message(Element[] elms) {

// make a defensive copy of the Element array

elements = new Element[elms.length];

for (int i=0; i < elms.length; i++) {

elements[i] = elms[i];

}

}

/**

Código de la Aplicación

JXTA sobre J2ME - 147 -

* Return the number of Elements contained in this Message. Usage:

*

* <p><code>

* for (int i=0; i < msg.getElementCount(); i++) { <br>

* &nbsp;&nbsp;Element el = msg.getElement(i); <br>

* &nbsp;&nbsp;... <br>

* }

* </code></p>

*

* @return the number of Elements in this Message.

*/

public int getElementCount() {

return elements.length;

}

/**

* Return the Element contained in this Message at the specified

* index. Usage:

*

* <p><code>

* for (int i=0; i < msg.getElementCount(); i++) { <br>

* &nbsp;&nbsp;Element el = msg.getElement(i); <br>

* &nbsp;&nbsp;... <br>

* }

* </code></p>

*

* @param index specifies the index of the Element to be returned

*

* @return the Elements in this Message at the specified index.

*

* @throws ArrayOutOfBoundsException if the specified index is out

* of bounds (negative or greater than {@link #getElementCount})

*/

public Element getElement(int index) {

return elements[index];

}

void write(DataOutputStream dos)

throws IOException {

// write message signature

for (int i=0; i < JXTA_MESSAGE_HEADER.length(); i++) {

dos.writeByte(JXTA_MESSAGE_HEADER.charAt(i));

}

// write message version

dos.writeByte(MESSAGE_VERSION);

// calculate namespace table indices

/* there can be at most elements.length namespaces. We specify

the Hashtable size because mostly, element count will be

around 3-5 and using the default Hashtable size of 11 would

be quite wasteful */

Hashtable ns2id = new Hashtable(elements.length);

int nsId = nextNameSpaceId;

for (int i=0; i < elements.length; i++) {

Código de la Aplicación

JXTA sobre J2ME - 148 -

String ns = elements[i].getNameSpace();

if (static_ns2id.get(ns) == null &&

ns2id.get(ns) == null) {

ns2id.put(ns, new Integer(nsId++));

}

}

// write message name spaces

dos.writeShort(ns2id.size());

Enumeration nse = ns2id.keys();

while(nse.hasMoreElements()) {

String ns = (String) nse.nextElement();

writeString(dos, ns);

}

// write message element count

int elementCount = getElementCount();

dos.writeShort(elementCount);

for (int i=0; i < elementCount; i++) {

// write message elements

getElement(i).write(dos, static_ns2id, ns2id);

}

}

static Message read(DataInputStream dis)

throws IOException {

// read message signature

for (int i=0; i < JXTA_MESSAGE_HEADER.length(); i++) {

if (dis.readByte() != JXTA_MESSAGE_HEADER.charAt(i)) {

throw new IOException("Message header not found");

}

}

// read message version

int version = dis.readByte();

if (version != MESSAGE_VERSION) {

throw new IOException("Message version mismatch: expected " +

MESSAGE_VERSION + ", got " + version);

}

// read message name spaces

int nsCount = dis.readShort();

String[] nameSpaces = new String[nsCount];

for (int i=0; i < nsCount; i++) {

String ns = readString(dis);

nameSpaces[i] = ns;

}

// read message element count

int elementCount = dis.readShort();

Element[] elms = new Element[elementCount];

// create name space indices

Hashtable id2ns = new Hashtable(nsCount);

for (int i=0; i < nsCount; i++) {

id2ns.put(new Integer(i), nameSpaces[i]);

Código de la Aplicación

JXTA sobre J2ME - 149 -

}

for (int i=0; i < elementCount; i++) {

// read message elements

elms[i] = Element.read(dis, static_id2ns, id2ns);

}

Message msg = new Message(elms);

return msg;

}

static String readString(DataInputStream dis)

throws IOException {

int len = dis.readShort();

if (len < 0) {

throw new IOException("Negative string length in message");

}

byte[] bytes = new byte[len];

dis.readFully(bytes);

return new String(bytes);

}

static void writeString(DataOutputStream dos, String s)

throws IOException {

dos.writeShort(s.length());

dos.write(s.getBytes());

}

/**

* Returns the size in bytes of this Message.

*/

public int getSize() {

synchronized(byteCounter) {

byteCounter.reset();

synchronized(dataCounter) {

try {

write(dataCounter);

} catch (IOException ex) {

throw new RuntimeException("ByteCounter should never " +

" throw an IOException");

}

}

return byteCounter.size();

}

}

}

Código de la Aplicación

JXTA sobre J2ME - 150 -

Código de la Aplicación

JXTA sobre J2ME - 151 -

/************************************************************************

* Clase Element:

*

* $Id: Element.java,v 1.2 2002/02/22 00:15:15 akhil Exp $

*

* Copyright (c) 2001 Sun Microsystems, Inc. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions

* are met:

*

* 1. Redistributions of source code must retain the above copyright

* notice, this list of conditions and the following disclaimer.

*

* 2. Redistributions in binary form must reproduce the above copyright

* notice, this list of conditions and the following disclaimer in

* the documentation and/or other materials provided with the

* distribution.

*

* 3. The end-user documentation included with the redistribution,

* if any, must include the following acknowledgment:

* "This product includes software developed by the

* Sun Microsystems, Inc. for Project JXTA."

* Alternately, this acknowledgment may appear in the software itself,

* if and wherever such third-party acknowledgments normally appear.

*

* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"

* must not be used to endorse or promote products derived from this

* software without prior written permission. For written

* permission, please contact Project JXTA at http://www.jxta.org.

*

* 5. Products derived from this software may not be called "JXTA",

* nor may "JXTA" appear in their name, without prior written

* permission of Sun.

*

* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED

* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR

* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF

* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,

* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT

* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

* SUCH DAMAGE.

*

* ====================================================================

*

* This software consists of voluntary contributions made by many

* individuals on behalf of Project JXTA. For more

* information on Project JXTA, please see

* <http://www.jxta.org/>.

*

* This license is based on the BSD license adopted by the Apache

Código de la Aplicación

JXTA sobre J2ME - 152 -

* Foundation.

**********************************************************************/

package net.jxta.j2me;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.util.Hashtable;

/**

* This class represents an Element of a JXTA {@link

* net.jxta.j2me.Message}. A JXTA Message is composed of several

* Elements.<p>

*

* This is an immutable class.

*/

public final class Element {

private static final String DEFAULT_MIME_TYPE = "application/octet-

stream";

// flags

private static final int HAS_TYPE = 0x01;

private static final String JXTA_ELEMENT_HEADER = "jxel";

private String name = null;

private byte[] data = null;

private String nameSpace = null;

private String mimeType = null;

/**

* Construct an Element from its parts.

*

* @param name the name of the Element

*

* @param data the data that this Element carries. This data is

* transported across the network as-is.

*

* @param nameSpace the name space used by the Element. JXTA

* messages use the <code>"jxta"</code> namespace. JXTA for J2ME

* messages use a private namespace. If namespace is

* <code>null</code>, the default namespace of <code>""</code> is

* used.

*

* @param mimeType the mimeType of the data. If <code>null</code>,

* the default MIME type of

* <code>"application/octet-stream"</code> is assumed.

*/

public Element(String name, byte[] data,

String nameSpace, String mimeType) {

if (name == null) {

throw new IllegalArgumentException("Element name cannot be

null");

Código de la Aplicación

JXTA sobre J2ME - 153 -

}

this.name = name;

if (data == null) {

throw new IllegalArgumentException("Element data cannot be

null");

}

this.data = data;

if (nameSpace == null) {

this.nameSpace = Message.DEFAULT_NAME_SPACE;

} else {

this.nameSpace = nameSpace;

}

if (mimeType == null) {

this.mimeType = DEFAULT_MIME_TYPE;

} else {

this.mimeType = mimeType;

}

}

void write(DataOutputStream dos,

Hashtable static_ns2id, Hashtable ns2id)

throws IOException {

// write element signature

for (int i=0; i < JXTA_ELEMENT_HEADER.length(); i++) {

dos.writeByte(JXTA_ELEMENT_HEADER.charAt(i));

}

// write element name space id

// initialize nsId to the index of Message.DEFAULT_NAME_SPACE

int nsId = 0;

Integer nsIdInt = (Integer) static_ns2id.get(nameSpace);

if (nsIdInt != null) {

nsId = nsIdInt.intValue();

} else {

nsIdInt = (Integer) ns2id.get(nameSpace);

if (nsIdInt != null) {

nsId = nsIdInt.intValue() + static_ns2id.size();

}

}

dos.writeByte(nsId);

// write element flags

byte flags = 0;

if (!DEFAULT_MIME_TYPE.equals(mimeType)) {

flags |= HAS_TYPE;

}

dos.writeByte(flags);

// write element name

Message.writeString(dos, name);

Código de la Aplicación

JXTA sobre J2ME - 154 -

// write element mime type

if ((flags & HAS_TYPE) != 0) {

Message.writeString(dos, mimeType);

}

// write element data

dos.writeInt(data.length);

dos.write(data);

}

static Element read(DataInputStream dis,

Hashtable static_id2ns, Hashtable id2ns)

throws IOException {

// read element signature

for (int i=0; i < JXTA_ELEMENT_HEADER.length(); i++) {

if (dis.readByte() != JXTA_ELEMENT_HEADER.charAt(i)) {

throw new IOException("Message element header not found");

}

}

// read element name space id

int nsId = dis.readByte();

Integer nsIdInt = new Integer(nsId);

String ns = (String) static_id2ns.get(nsIdInt);

if (ns == null) {

nsId -= static_id2ns.size();

nsIdInt = new Integer(nsId);

ns = (String) id2ns.get(nsIdInt);

if (ns == null) {

throw new IOException("Namespace not found for id " + nsId);

}

}

// read element flags

byte flags = dis.readByte();

// read element name

String name = Message.readString(dis);

// read element mime type

String mimeType = DEFAULT_MIME_TYPE;

if ((flags & HAS_TYPE) != 0) {

mimeType = Message.readString(dis);

}

// read element data

int len = dis.readInt();

byte[] data = new byte[len];

dis.readFully(data);

return new Element(name, data, ns, mimeType);

}

/**

Código de la Aplicación

JXTA sobre J2ME - 155 -

* Return the name of the Element.

*

* @return the Element name

*/

public String getName() {

return name;

}

/**

* Return the namespace used by the Element name.

*

* @return the Element namespace

*/

public String getNameSpace() {

return nameSpace;

}

/**

* Return the MIME type of the data in the Element.

*

* @return the Element MIME type

*/

public String getMimeType() {

return mimeType;

}

/**

* Return the data in the Element.

*

* @return the Element data

*/

public byte[] getData() {

return data;

}

/**

* Return a String representation of the Element.

*

* @return a string representation of the Element

*/

public String toString() {

StringBuffer sb = new StringBuffer();

sb.append("\"");

sb.append(nameSpace);

sb.append(":");

sb.append(name);

sb.append("\" \"");

sb.append(mimeType);

sb.append("\" dlen=");

sb.append(Integer.toString(data.length));

return sb.toString();

}

}

Código de la Aplicación

JXTA sobre J2ME - 156 -

Código de la Aplicación

JXTA sobre J2ME - 157 -

/************************************************************************

* Clase ByteCounterOutputStream:

*

* $Id: ByteCounterOutputStream.java,v 1.1 2002/03/21 05:24:04 akhil Exp $

*

* Copyright (c) 2001 Sun Microsystems, Inc. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions

* are met:

*

* 1. Redistributions of source code must retain the above copyright

* notice, this list of conditions and the following disclaimer.

*

* 2. Redistributions in binary form must reproduce the above copyright

* notice, this list of conditions and the following disclaimer in

* the documentation and/or other materials provided with the

* distribution.

*

* 3. The end-user documentation included with the redistribution,

* if any, must include the following acknowledgment:

* "This product includes software developed by the

* Sun Microsystems, Inc. for Project JXTA."

* Alternately, this acknowledgment may appear in the software itself,

* if and wherever such third-party acknowledgments normally appear.

*

* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"

* must not be used to endorse or promote products derived from this

* software without prior written permission. For written

* permission, please contact Project JXTA at http://www.jxta.org.

*

* 5. Products derived from this software may not be called "JXTA",

* nor may "JXTA" appear in their name, without prior written

* permission of Sun.

*

* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED

* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR

* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF

* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,

* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT

* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

* SUCH DAMAGE.

*

* ====================================================================

*

* This software consists of voluntary contributions made by many

* individuals on behalf of Project JXTA. For more

* information on Project JXTA, please see

* <http://www.jxta.org/>.

*

* This license is based on the BSD license adopted by the Apache

* Foundation.

Código de la Aplicación

JXTA sobre J2ME - 158 -

**********************************************************************/

package net.jxta.j2me;

import java.io.OutputStream;

import java.io.IOException;

/**

* This class extends OutputStream to implement the functionality of

* counting bytes without actually having to buffer the stream in

* memory. This allows us to pre-compute the size of a Message, for

* example, to set the Content-Length HTTP header in an outgoing HTTP

* request.

*/

final class ByteCounterOutputStream extends OutputStream {

int size = 0;

public ByteCounterOutputStream() {

super();

size = 0;

}

/**

* Reset the state of the class so as to be able to reuse the same

* Object over and over.

*/

public void reset() {

size = 0;

}

/**

* Returns the size of the Objects serialized so far.

*

* @return the size in bytes of Objects serialized so far

*/

public int size() {

return size;

}

public void write(byte[] b) throws IOException {

size += b.length;

}

public void write(byte[] b, int off, int len) throws IOException {

size += len;

}

public void write(int b) throws IOException {

size += 1;

}

public void close() throws IOException {

super.close();

}

Código de la Aplicación

JXTA sobre J2ME - 159 -

public void flush() throws IOException {

super.flush();

}

}

Código de la Aplicación

JXTA sobre J2ME - 160 -

/************************************************************************

* Clase HttpMessenger:

*

* $Id: HttpMessenger.java,v 1.25 2003/05/20 01:52:00 kuldeep Exp $

*

* Copyright (c) 2001 Sun Microsystems, Inc. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions

* are met:

*

* 1. Redistributions of source code must retain the above copyright

* notice, this list of conditions and the following disclaimer.

*

* 2. Redistributions in binary form must reproduce the above copyright

* notice, this list of conditions and the following disclaimer in

* the documentation and/or other materials provided with the

* distribution.

*

* 3. The end-user documentation included with the redistribution,

* if any, must include the following acknowledgment:

* "This product includes software developed by the

* Sun Microsystems, Inc. for Project JXTA."

* Alternately, this acknowledgment may appear in the software itself,

* if and wherever such third-party acknowledgments normally appear.

*

* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"

* must not be used to endorse or promote products derived from this

* software without prior written permission. For written

Código de la Aplicación

JXTA sobre J2ME - 161 -

* permission, please contact Project JXTA at http://www.jxta.org.

*

* 5. Products derived from this software may not be called "JXTA",

* nor may "JXTA" appear in their name, without prior written

* permission of Sun.

*

* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED

* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR

* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF

* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,

* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT

* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

* SUCH DAMAGE.

*

* ====================================================================

*

* This software consists of voluntary contributions made by many

* individuals on behalf of Project JXTA. For more

* information on Project JXTA, please see

* <http://www.jxta.org/>.

*

* This license is based on the BSD license adopted by the Apache

* Foundation.

Código de la Aplicación

JXTA sobre J2ME - 162 -

**********************************************************************/

package net.jxta.j2me;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import javax.microedition.io.Connector;

import javax.microedition.io.HttpConnection;

/**

* Provides a messaging service for the JXTA for MIDP peer. Facilities

* to send and receive messages are provided. Message receiving is

* based on establishing a relationship with a JXTA relay server.

*/

final class HttpMessenger {

/**

* The duration that the client is willing to for the

* connection to block until there is a message to be

* sent. Setting it to -1 means do not block. Setting

* it to 0 would mean wait forever, but the server will

* never comply: it caps it at some tunable value

* (120000 actually).

*/

/**

* Block until Peer ID and Lease are obtained

Código de la Aplicación

JXTA sobre J2ME - 163 -

*/

private static final int DEFAULT_CONNECT_POLL_TIMEOUT = 0;

/**

* The duration that the client is willing to block for

* more messages on the same connection (using

* chunking). If set to -1, the connection closes after

* one message.

* TBD: Not Used

*/

//private static final int DEFAULT_CLIENT_WAIT_TIMEOUT = 2000;

/**

* Requested lease duration.

*/

private static final int DEFAULT_CLIENT_LEASE = 3600000;

private static final String RELAY_SERVICE_ID =

"uuid-DEADBEEFDEAFBABAFEEDBABE0000000F05";

private static final String ENDPOINT_SERVICE_ID ="EndpointService:jxta-

NetGroup";

private static final String COMMAND_CONNECT = "connect";

private static final String COMMAND_GET_PID = "pid";

private static final String UNKNOWN_PID = "unknown-unknown";

/** the relay that we are connecting to **/

private String relayUrl = null;

Código de la Aplicación

JXTA sobre J2ME - 164 -

/** our peerid **/

private String peerId = null;

HttpMessenger() {

}

private String constructURL(String initRelayUrl,

String command,

int timeout,

String pid) {

String url = initRelayUrl + // Relay URL

(http://address:port)

"/" + pid +

"?" + Integer.toString(timeout) + // Relay Poll time -

keeps connection alive

"," + Integer.toString(-1) + // lazytime out -

always -1 for JXME peers

"," + initRelayUrl + // Relay URL

(http://address:port)

"/" + ENDPOINT_SERVICE_ID + // Endpoint

Service's ID

"/" + RELAY_SERVICE_ID + // Relay Service's

ID

"/" + command; // actual command

for relay service

if (COMMAND_CONNECT.equals(command)) {

url += "," +

Integer.toString(DEFAULT_CLIENT_LEASE) + "," +

"keep,other";

Código de la Aplicación

JXTA sobre J2ME - 165 -

}

System.out.println ("relay: " + url);

return url;

}

/** connects to the relay service and hands back the peer id **/

synchronized String connect(String initRelayUrl, String persistedPeerId)

throws IOException {

if (initRelayUrl == null) {

throw new IOException("No relay URL specified");

}

relayUrl = initRelayUrl;

peerId = persistedPeerId;

String relay = null;

if (peerId == null) {

relay = constructURL(initRelayUrl,

COMMAND_GET_PID,

DEFAULT_CONNECT_POLL_TIMEOUT,

UNKNOWN_PID);

} else {

relay = constructURL(initRelayUrl,

COMMAND_CONNECT,

DEFAULT_CONNECT_POLL_TIMEOUT,

peerId);

}

Código de la Aplicación

JXTA sobre J2ME - 166 -

HttpConnection conn = null;

DataInputStream dis = null;

try {

conn = (HttpConnection) Connector.open(relay,

Connector.READ_WRITE);

conn.setRequestMethod(HttpConnection.GET);

conn.setRequestProperty("Connection", "close");

conn.setRequestProperty("Content-Length", "0");

if (conn.getResponseCode() != HttpConnection.HTTP_OK &&

conn.getResponseCode() != 100) {

throw new IOException("HTTP Error: " +

conn.getResponseCode() + " " +

conn.getResponseMessage());

}

if (conn.getLength() <= 0) {

return null; // return null as this could be denial of

lease.

}

dis = conn.openDataInputStream();

Message msg = Message.read(dis);

boolean hasPidResponse = false;

for (int i=0; i < msg.getElementCount(); i++) {

Element el = msg.getElement(i);

String data = new String(el.getData());

Código de la Aplicación

JXTA sobre J2ME - 167 -

if (Message.RESPONSE_TAG.equals(el.getName()) &&

COMMAND_GET_PID.equals(data)) {

hasPidResponse = true;

break;

}

}

for (int i=0; hasPidResponse && i < msg.getElementCount(); i++) {

Element el = msg.getElement(i);

if ("peerid".equals(el.getName())) {

peerId = new String(el.getData());

peerId = peerId.substring (peerId.indexOf("uuid"));

// now that we have a peerid, connect and get a lease

// caution: recursive call

if (peerId != null) {

connect(initRelayUrl, peerId);

}

break;

}

}

} finally {

if (dis != null) {

dis.close();

}

if (conn != null) {

conn.close();

}

}

Código de la Aplicación

JXTA sobre J2ME - 168 -

return peerId;

}

/**

* Polls for a message from the relay. This is a blocking call. If a

* message is not received within the timeout value, null is returned

*/

synchronized Message poll(int timeout, Message outgoing)

throws IOException {

Message msg = null;

HttpConnection conn = null;

DataInputStream dis = null;

DataOutputStream dos = null;

String relay = null;

System.out.println ("Polling Interval: " + timeout);

try {

relay = constructURL (relayUrl, "", timeout, peerId);

conn = (HttpConnection) Connector.open(relay,

Connector.READ_WRITE);

conn.setRequestMethod(HttpConnection.POST);

conn.setRequestProperty("Connection", "close");

if (outgoing == Message.EMPTY) {

conn.setRequestProperty("Content-Length", "0");

} else {

conn.setRequestProperty("Content-Length",

Integer.toString(outgoing.getSize()));

Código de la Aplicación

JXTA sobre J2ME - 169 -

//System.out.println ("send Con-Len " + outgoing.getSize());

dos = conn.openDataOutputStream();

outgoing.write(dos);

}

if (conn.getResponseCode() != HttpConnection.HTTP_OK &&

conn.getResponseCode() != 100) {

throw new IOException("HTTP Error: " +

conn.getResponseCode() + " " +

conn.getResponseMessage());

}

System.out.println ("recv Con Length: " + conn.getLength());

if (conn.getLength() <= 0) {

return null;

}

dis = conn.openDataInputStream();

msg = Message.read(dis);

} catch (Throwable t) {

System.out.println ("Error polling: " + t.getMessage());

} finally {

if (conn != null) {

conn.close();

}

if (dos != null) {

dos.close();

}

if (dis != null) {

dis.close();

Código de la Aplicación

JXTA sobre J2ME - 170 -

}

}

return msg;

}

}