Page 1

ID O

GR IN AT CL IS U

CD

LA PRIMERA REVISTA DE PROGRAMACIÓN EN CASTELLANO

Precio: 6 € (España) (IVA incluido)

AÑO XI. 2.ª ÉPOCA

• Nº 127 •

UNA PUBLICACIÓN DE:

REVISTAS PROFESIONALES S.L.

ACTUALIDAD Presente y futuro de IPv6 Java Expo 2005 Inteligencia Ambiental: la presencia invisible

DISEÑO Generación de código a partir de modelos con EMF

MIDDLEWARE Tipos Generics en .NET 2.0 para C# y VB Struts, la referencia en desarrollos J2EE

CANAL PANDA La seguridad IT, también para los empleados

ALGORITMOS Programación Win32 avanzada

REDES Programación web ágil con ColdFusion MX 7

00127

Noticias, javaHispano y Opinión, Libros, Preguntas y Respuestas

8

413042 303299


EDITORIAL Número 127 Ed i t a : R EVISTAS PROFESIONALES S.L. solop@revistasprofesionales.com C/ Valentin Beato 42,3ª.28037 - Madrid. http://www.revistasprofesionales.com http://digital.revistasprofesionales.com E d i to r Agustín Buelta •••••••••••••••••••••••••••••••••• Co o r d i n a c i ó n T é c n i c a - R e d a c c i ó n Carlos Laparra •••••••••••••••••••••••••••••••••• Maquetación Alfonso Sabán Mejías •••••••••••••••••••••••••••••••••• As e s o r í a d e Pu b l i c i d a d Felipe Ribagorda Tel.:91 304 87 64 D e l e g a c i ó n e n B a r ce l o n a C/ Rocafort,241/243,5º 1ª Mariano Sánchez Tel.:93 322 12 38 •••••••••••••••••••••••••••••••••• S u s c ri pc i o n e s Tel:91 304 87 64 Fax:91 327 13 03 ••••••••••••••••••••••••••••••••••• I m p re s i ó n Ideas de Impresión ••••••••••••••••••••••••••••••••••• D i s t ri b u c i ó n Motorpress Ibérica

P2P, UN ENFOQUE DIFERENTE Cuando se nos habla de las redes Peer to Peer (P2P) no podemos evitar pensar en la descarga de vídeo o música. Hay quien dice que dicha descarga constituye un delito, aunque por otro lado hay quien defiende el derecho a compartir. Al margen de esta discusión, lo que no genera ningún tipo de dudas es que la tecnología P2P alberga grandes posibilidades de comunicación y por lo tanto de negocio. Microsoft, por ejemplo, ha apostado por esta tecnología en el proyecto Farsite (http://research.microsoft.com/sn/Farsite/). Farsite es un sistema de ficheros distribuido que permite a un grupo de estaciones de trabajo acceder a un sistema de ficheros único y que reside en las propias estaciones de trabajo, de modo que cada estación desempeña las labores de cliente y servidor, tal como establece el concepto de P2P. Mediante un sofisticado sistema de replicación, los archivos del sistema de ficheros se van almacenando y replicando por las estaciones de trabajo que tienen acceso a dicha infraestructura. Para el caso de plataformas GNU/Linux, la empresa Radiant Data ha desarrollado PeerFS (http://www.radiantdata.com), un sistema de ficheros igualmente distribuido y replicado de forma transparente en los equipos conectados. Además, PeerFS también permite distribuir las bases de datos de MySQL, lo cual incorpora el concepto P2P al mundo del almacenamiento masivo de datos. Esto son sólo dos ejemplos que demuestran que la tecnología P2P va a centrar gran parte de la innovación en los próximos años, lo que conduce a pensar que la revolución provocada por Napster en su momento, aún no ha acabado…

SUMARIO ACTUALIDAD 12

D I S T R I B U C I O N E N M E X I CO DIMSA - C/ Mariano Escobedo,218 Col.Anáhuac.11320 México,D.F. D I S T R I BUCION EN ARG E N T I N A Capital Federal:Distrimachisa Interior:York Agencysa - Tlf:(5411) 433 150 51 R E P R E S E N TANTE EN MEXICO Angel Bosch - angelbosch@infosel.net.mx Distribución,números atrasados y suscripciones C/ Renacimiento,180.Col.San Juan Tlihuaca. Azcapotzalco.02400 México D.F. •••••••••••••••••••••••••••••••••• La revista Sólo Programadores no tiene por qué estar de acuerdo con las opiniones escritas por sus colaboradores en los artículos firmados. El editor prohibe expresamente la reproducción total o parcial de los contenidos de la revista sin su autorización escrita. Depósito legal:M-26827-1994 P R I N T E D I N S PA I N COPYRIGHT 30-06-2005 P.V.P.6,00 Euros Precio en Canarias,Ceuta y Melilla: 6,15 Euros

Asociación Española de Editoriales de Publicaciones Periódicas

14 16

IPv6: Oportunidad para la innovación, oportunidad para nuevos negocios Java Expo 2005 Inteligencia Ambiental: la presencia invisible

DISPOSITIVOS MÓVILES 20

Creación de un sistema de distribución de Midlets (I)

MIDDLEWARE 26 32

Novedades en los lenguajes de .NET 2.0 (y III) Struts práctico (II)

REDES 42

Creación de aplicaciones web con ColdFusion MX 7 (II)

DISEÑO 48

Generación de código a partir de modelos con EMF

ALGORITMOS 56

API Win32 y C: una programación directa y eficaz (y III)

Y ADEMÁS. . . 04 08 10 62 64 66

Noticias javaHispano: Harmony, ejecutar .NET en J2EE, juegos con Java y más Canal Panda: La seguridad IT, también para los empleados Preguntas y respuestas Contenido del CD-ROM Libros: Gráficos y BBDD


NOTICIAS

MICROSOFT

La nueva versión de Microsoft Office, “Office 12”, incorporará XML como formato para sus archivos Microsoft ha anunciado recientemente que adoptará la tecnología estándar de la industria eXtensible Markup Language (XML) como formato de archivo por defecto en la próxima versión de las ediciones de Microsoft Office, actualmente conocida con el nombre en clave de “Office 12”. Los nuevos formatos de archivo, llamados Microsoft Office Open XML Formats, se convertirán en los formatos por defecto para las versiones de Microsoft Word, Excel y PowerPoint de “Office 12”, que espera ser lanzado al mercado en la segunda mitad de 2006. Para que los desarrolladores, partners y profesionales de las nuevas tecnologías dispongan de las herramientas e información que necesitan para desarrollar soluciones basadas en Office utilizando los nuevos formatos de archivo, Microsoft irá emitiendo la información necesaria, así como conferencias y eventos relacionados con este tema (como por ejemplo Tech·Ed 2005). Las ventajas que pueden derivarse de la incorporación de la tecnología XML a los formatos de los archivos de “Office 12” son:  Formatos de archivo compactos y robustos.  Mejor interoperabilidad de datos.  Formatos abiertos y sin royalties.  Compatibilidad y soporte para clientes y partners.

jo XML dentro de los formatos de archivo HTML soportados por Word, Excel y PowerPoint. Microsoft ha ampliado ese soporte en Office XP y Office 2003, además de en Microsoft Office InfoPath 2003. También los clientes y la industria de la tecnología en general han adoptado rápidamente XML. Según una investigación de Microsoft, más de un millón de desarrolladores están actualmente trabajando en soluciones sobre Office 2003, y más de un tercio de ellos están utilizando XML en estas soluciones. En un estudio realizado por Gartner, se estima que la utilización de eforms habilitadas por XML al menos se duplicará durante el próximo año y que en el año 2007, un 40% de trabajadores cualificados usarán herramientas para la creación de contenidos XML. Otro estudio desarrollado por Forrester Research prevé que en 2008 XML se convertirá en uno de los formatos de documento dominantes para el archivo de datos. Las personas interesadas en los nuevos formatos de archivo de la próxima versión de Office pueden obtener información adicional en la web http://www.microsoft.com/office/preview.

La gran aceptación de XML Microsoft ha dado soporte a este estándar de la industria en su tecnología de productividad desde Office 2000, cuando la compañía introdu-

BEA SYSTEMS

Gartner sitúa a BEA como proveedor líder de software de infraestructura BEA Systems ha dado a conocer recientemente los resultados de un estudio de Gartner, del que se desprende que la propia compañía se sitúa como uno de los tres proveedores más importantes en tres diferentes mercados durante 2004: servidores de aplicaciones, productos de portales y suites de aplicaciones, así como en AIM (Aplicación, Integración y Middleware) y en mercado de Portal. Para hacer tal afir-

BUSINESS OBJECTS

Crystal Reports XI y Crystal Reports Server XI, disponibles en castellano Business Objects, proveedor de soluciones de business intelligence (BI), acaba de lanzar la nueva versión en castellano de sus dos últimos productos, Crystal Reports XI y Crystal Reports Server XI. La experiencia adquirida y las impresiones favorables que le han llegado con respecto al producto en castellano de la anterior versión 10 han dado lugar a este nuevo paso por parte de la compañía. SOLO PROGRAMADORES nº 127

4

mación, el estudio se ha basado en la cuota de mercado de ingresos por licencias. El informe de Gartner “Market Share: AIM and Portal Software, Worldwide, 2004, Preliminary” muestra que BEA consiguió en 2004 el 21,7% del mercado mundial de servidores de aplicaciones; el 11,6% del mercado de suites de aplicaciones; el 8,9% de productos de portal y el 7,2% del mercado de integración y middleware, mercado, éste último, cuyo valor asciende a más de 6.700 millones de dólares. Gartner evalúa a los proveedores teniendo en cuenta su visión y capacidad de ejecución. La firma de investigación entiende por “líderes” aquellos fabricantes que presentan un buen funcionamiento, tienen una clara visión de las tendencias del mercado y son proactivos en la construcción de competencias para mantener sus posiciones de liderazgo en el mercado. De hecho, la mayoría de las ventas que la compañía realizó de este producto eran de ediciones en castellano, donde un 72% de las unidades vendidas en 2004 eran referencias en castellano, mientras que el 28% eran ediciones en inglés que se distribuían tanto a España como para Portugal, no dejando duda de que el mercado prefiere el producto traducido. Este ha sido el principal motivo que ha llevado a Business Objects a lanzar la nueva versión de Crystal Reports XI y Crystal Reports Server XI. http://digital.revistasprofesionales.com


Sólo Programadores en Formato Digital Por menos dinero Llegará antes a su ordenador que a los quioscos Entra en http://digital.revistasprofesionales.com Suscripción anual a Sólo Programadores por sólo 27 euros y a Sólo Programadores y Mundo Linux por sólo 40 euros Regalo de un CD-ROM con el archivo de los 12 ejemplares de la temporada 2003-04


NOTICIAS

PANDA SOFTWARE

La PYME española carece de protección frente a las nuevas amenazas Panda Software ha presentado recientemente su informe “Niveles de Protección en la PYME española 2005”, en el marco de la “Campaña de Seguridad para la PYME”, que, promovida por la Asociación de Internautas (AI) y con el apoyo de la Entidad Pública Red.es, cuenta con la colaboración de Panda Software. Dicho informe pone de manifiesto que si bien ha aumentado satisfactoriamente el nivel de protección antivirus, las PYMES no están adecuadamente protegidas contra las nuevas amenazas como el spyware y el spam, que son, después de los virus, las que más preocupan a los pequeños empresarios españoles. Según los datos que se desprenden de este estudio, el 94,5% de las PYMES asegura contar con un sistema de seguridad, siendo un antivirus el 60,5% de los casos. Asimismo, de las empresas encuestadas que tienen implantado un software antivirus, el 96% asegura tenerlo actualizado. Estos datos suponen un gran avance en materia de seguridad en cuanto a antivirus se refiere, pero hay más.

Desprotección frente a las nuevas amenazas El informe 2005 presenta sin embargo un panorama bien distinto en cuanto a las nuevas amenazas se refiere y la conclusión que de él se extrae es que la PYME española cree que con disponer de un antivirus es suficiente, ya que sólo un 10% de las empresas entrevistadas dispone de un sistema de seguridad antispyware. Estos datos son preocupantes si se tiene en cuenta que a nivel mundial el 90% por ciento de las empresas tiene software espía instalado según un informe elaborado por las empresas Webroot y Earthlink. Además, la media de spyware instalado en cada uno de los equipos analizados es de 25 ejemplares. Según los datos recogidos por la solución antivirus online y gratuita de Panda Software, Panda ActiveScan

SUN MICROSYSTEMS

Telefónica móviles despliega sus servicios con Java Enterprise System Sun Microsystems ha anunciado que Telefónica Móviles España (TME) ha seleccionado el software de infraestructura Java Enterprise System (JES) tanto para ofrecer servicios existentes como para desplegar aplicaciones y servicios de próxima generación. La solución Java Enterprise System va a proporcionar a Telefónica un conjunto completo de componentes abiertos y basados en estándares que facilitan la rápida creación y despliegue de nuevos servicios de comunicaciones. Los beneficios para TEM, según fuentes de Sun, pueden resumirse en un ahorro de costes por el innovador modelo de licencia (precio fijo al año por empleado), no penalizar económicamente los servicios cuando crece el número de clientes, derecho a un número ilimitado de licencias de los productos que forman parte de JES, etc.

Java Enterprise System Java Enterprise System pretende ser una simplificación del software de importancia crítica para las empresas,

SOLO PROGRAMADORES nº 127

6

(cuya nueva versión detecta spyware), el 84% del total de malware instalado en los ordenadores es software espía. El informe 2005 presentado por Panda revela que, en el caso del spam, la situación de las PYMES es aún, de mayor desprotección que en el caso del spyware, ya que solamente el 5,5% de las PYMES aseguran contar con un sistema de seguridad específico para combatirlo. Según datos hechos públicos recientemente por la empresa TB-Security se calcula que el spam causa pérdidas de productividad que alcanzan el 3%. Lo cual puede dar una idea del impacto que dicho tipo de malware puede producir en las PYMES españolas si se tienen en cuenta las estimaciones de la propia TB-Security que pronostica un 30% en el crecimiento del spam para 2005.

Mayor nivel de información y concienciación Sin embargo, el informe de Panda Software también refleja algunos aspectos positivos. Por ejemplo, es destacable que las PYMES españolas parecen más y mejor informadas sobre las posibles amenazas y vulnerabilidades existentes, puesto que más de la mitad de los usuarios busca asesoramiento en empresas especializadas y proveedores de seguridad informática en caso de tener un problema.

Muestra del Informe El informe ha sido realizado en el segundo trimestre de 2005 y se centra en una muestra de 1.253 PYMES, clasificada por tamaño de la empresa (de 0 a 10 empleados, de 11 a 25 empleados, de 26 a 50 empleados, y más de 50 empleados). Además, se ha elaborado un estudio detallado de seguridad en las principales Comunidades Autónomas (Andalucía, Cataluña, Comunidad Valenciana, Madrid y País Vasco). integrando los servicios corporativos de red más habituales y que las organizaciones necesitan para crear y desplegar con éxito sus aplicaciones de negocio. Este completo sistema de software basado en estándares se entrega como una única entidad sobre infraestructura preintegrada, con ciclos de actualizaciones predecibles y un coste muy asequible (140 euros por usuario al año). Supuestamente, JES proporciona a los responsables de sistemas la tranquilidad que necesitan para centrarse únicamente en lo que mejor saben hacer: utilizar las Tecnologías de la Información para lograr metas de negocio. Los servicios en red compartidos de JES proporcionan las siguientes funcionalidades y capacidades:  Servicios de aplicaciones y Web  Servicios de identidad de red  Servicios de portal  Servicios de comunicación y colaboración  Servicios de disponibilidad  Servicios de seguridad de extremo a extremo El lector puede obtener más información sobre Java Enterprise System en www.sun.com/ software/javaenterprisesystem.

http://digital.revistasprofesionales.com


NOTICIAS

RED.ES y ARSYS

Disponible la guía sobre la nueva normativa para el registro de dominios .es La entidad pública empresarial Red.es, adscrita al Ministerio de Industria, Turismo y Comercio de España, y arsys, empresa de registro y alojamiento de dominios, han editado conjuntamente la Guía de dominios “.es”, un manual que recoge los aspectos básicos incluidos en el nuevo Plan Nacional de Nombres de Dominio bajo el código correspondiente a España. El Boletín Oficial del Estado (BOE) del pasado 31 de mayo publicó la Orden de 19 de mayo por la que se aprueba este Plan, cuyo objetivo es flexibilizar las normas exigibles para la asignación de nombres de dominio bajo “.es” entre empresas, particulares e instituciones, y fomentar el desarrollo de la Sociedad de la Información en España. La Guía de dominios “.es” explica las nuevas condiciones de contratación de los registros territoriales, así como toda la informaCrecimiento de la demanda ción relativa a los requisitos necesarios para de dominios “.es” el registro, los plazos de implantación, el periodo de pre-registro, etc. La Guía puede descargarse en formato PDF desde la página web de arsys (www.arsys.es). Asimismo, la web del Esnic

(www.esnic.es), departamento de Red.es que gestiona la asignación de nombres de dominio bajo “.es”, incluye también un apartado específico sobre la nueva normativa.


JAVAHISPANO

Actualidad Java de la mano de javaHispano Apache podría desarrollar una implementación libre de J2SE Apache Software Foundation está estudiando desarrollar una implementación libre de J2SE, cuyo nombre sería Harmony. La iniciativa cuenta con el apoyo de numerosas figuras destacadas del Software Libre; entre ellas, miembros del equipo de desarrollo de IKVM, Kaffe, GCJ, Classpath y otros proyectos que implementan alguna parte de J2SE bajo una licencia libre. Varios representantes de Sun han manifestado su apoyo al proyecto, al cual es posible que contribuyan de algún modo. No obstante, también han afirmado que no comprenden la necesidad de esta implementación, ya que para ellos las licencias bajo las que se distribuye J2SE son suficientemente libres. Hasta que Harmony o javalí (http://www.javali.org.br), una iniciativa similar de origen brasileño, se completen, aquellos que deseen emplear una plataforma Java libre pueden optar por SNAP (http://www.snapplatform.org/), una solución que empaqueta en un mismo producto bien documentado y de fácil instalación a Jikes como compilador, SableVM como máquina virtual, GNU Classpath como librerías, Eclipse como IDE y Tomcat como servidor web.

El JDK 5.0 se distribuye bajo una nueva licencia Sun ha empezado a licenciar el JDK 5.0 bajo una nueva licencia: JIUL (Java Internal Use License). Esta licencia permitirá modificar el código fuente del JDK para corregir rápidamente bugs, sin la necesidad de esperar por los parches oficiales. Al código modificado no se le requerirá pasar los tests de compatibilidad de la plataforma. Estos JDKs modificados deberán utilizarse única y exclusivamente para uso interno, esto es, para cualquier uso operacional o comercial dentro de una organización o negocio. El acceso de clientes a la tecnología Java modificada a través de un cliente remoto (como un navegador web) se considera uso interno mientras el código modificado se halle y se ejecute en un servidor situado en el interior de la empresa. Esta es la tercera modalidad de licenciamiento del JDK de Sun, junto con la Sun Community Source License, orientada al mundo comercial, y Java Research License, orientada a la investigación y al mundo académico.

Grasshopper: ejecutar aplicaciones .NET en servidores J2EE Mainsoft ha lanzado un plugin para Visual Studio.NET, Grasshopper, que permite desplegar aplicaciones de .NET en servidores de aplicaciones Java. Para ello transforma, mediante un compilador, el MSIL de .NET en bytecode y, para proporcionar librerías como ASP.NET y ADO.NET, incorpora las librerías del proyecto Mono compiladas a bytecode. Existe una versión gratuita de Grasshopper que sólo permite desplegar las aplicaciones en Tomcat. Para emplear otros servidores J2EE y acceder a la funcionalidad de depuración del plugin es necesario ir a la versión comercial.

SOLO PROGRAMADORES nº 127

8

http://digital.revistasprofesionales.com


JAVAHISPANO

javaHispano

Soluciones para controlar cámaras digitales y escáneres desde Java

La popularidad de las cámaras digitales ha creado la demanda de un software que permita la descarga, visualización, clasificación y búsqueda de las fotos. Existen varias soluciones para desarrollar este tipo de programas desde Java; las más potentes son, probablemente, las desarrolladas por Aprise (http://asprise.com). Para el mundo Windows ofrece JTwain, mientras que para otros sistemas operativos debemos emplear JSANE, con soporte para AIX, BSD, GNU/Linux, OS2, Solaris y otros sistemas Unix. Otra solución es Morena (http://www.gnome.sk) que también permite interactuar con Twain y SANE desde Java. En el bando del Software Libre, podemos encontrar una solución para Windows, todavía en desarrollo, que comparte nombre con su homólogo de Aprise: JTwain (http://sourceforge.net/projects/jtwain/).

Struts cumple cinco años Struts, el primer framework web libre para Java, acaba de cumplir cinco años. Este framework ha sido el estándar de facto para desarrollo de aplicaciones web con Java durante muchos años y, en la actualidad, a pesar de la dura competencia de otros como Spring, sigue siendo líder indiscutible en cuanto a su uso. Desde que Craig R. McClanahan construyó la primera versión en su portátil mientras se encontraba de vacaciones, Struts ha experimentado muchas evoluciones. Pero la más radical está por llegar con el nacimiento de Shale, donde se romperá el monolítico controlador en tres partes y se contemplará el uso de JSF como capa de presentación.

OPINIÓN

Java para desarrollar juegos Cuando oímos Java y juegos en una misma frase tendemos a llevarnos las manos a la cabeza: que si el tiempo de inicio de la máquina virtual, que si la latencia del recolector de basura, que si el uso excesivo de memoria, que si es lento... La lista es casi interminable. Pero… ¿Existen de verdad razones que excluyan a Java como un lenguaje de desarrollo de videojuegos? En el equipo de Arianne (http://arianne.sourceforge.net/) pensamos que no. Cuando comenzamos el desarrollo de Stendhal, pensamos que Java sería la mejor opción porque aporta un entorno completo donde ejecutar nuestro videojuego independientemente del sistema operativo. Hemos probado el desarrollo multiplataforma en otros lenguajes y sin duda Java es el rey en este aspecto. El tiempo de inicio de la máquina virtual esta rondando entre los 1 o 2 segundos lo cual si vas a hacer un juego que enganche durante horas no es un problema. El consumo de memoria de nuestra aplicación es moderado, rondando los 30MB. Videojuegos similares a Stendhal desarrollados por equipos similares rondan esta cantidad o incluso la superan. No hemos experimentado problemas con el recolector de basura a pesar de no estar optimizado nuestro juego para evitar derrochar memoria, y aun así no afecta al tiempo de respuesta. Además, evitamos las fugas de memoria y por tanto reducimos la cantidad de memoria usada. Pero sobre todo, lo más importante para considerar la experiencia un éxito es la velocidad: velocidad de ejecución ya que sin ningún tipo de optimización hemos obtenido 120 FPS para una pantalla de 640x480x32 bits. Inicialmente pensamos en un error en el cálculo de los FPS, pero no, eran correctos: Java estaba creando la escena 120 veces por segundo. Por último la velocidad de desarrollo, que muchos parecen olvidar, ha sido lo que más nos ha sorprendido, escribir código Java nos ha resultado entre 2 y 3 veces más rápido que escribir código en C++ y casi a la par que escribir código en Python, con lo cual hemos empleado ese tiempo extra en desarrollar nuevas ideas y corregir los fallos existentes. Por tanto, a modo de conclusión, diría que la próxima vez que penséis en desarrollar un videojuego, dadle una oportunidad a Java; no os defraudará. Miguel Angel Blanch (mblanch) Desarrollador de Arianne

Sobre el autor Abraham Otero (abraham.otero@javahispano.org) es responsable de calidad y miembro de la junta de javaHispano. http://digital.revistasprofesionales.com

9

SOLO PROGRAMADORES nº 127


CANAL PANDA

La seguridad IT, también para los empleados FERNANDO DE LA CUADRA

Un sistema de seguridad no será efectivo si no se tienen en cuenta todas las posibles amenazas. Analicemos dónde pueden estar los puntos débiles de los sistemas de seguridad actualmente implantados.

Si no tenemos una concepción global de la seguridad podemos generar huecos muy difíciles de taponar SOLO PROGRAMADORES nº 127

10

Las normativas sobre seguridad e higiene en el trabajo tienen un fin muy claro: evitar accidentes y enfermedades en los trabajadores. Y además, cada normativa puede verse desde dos puntos de vista muy distintos, desde el lado del trabajador y desde el del empresario. Si un obrero utiliza casco, está protegiendo su integridad física, lo que sin duda es muy conveniente. Pero si un empresario hace que el trabajador use el casco, está protegiendo su fuerza de trabajo, haciendo que la empresa no vea reducida su productividad. Más allá de estos puntos de vista, hay un trasfondo añadido a estas medidas de seguridad: en una empresa segura, el ambiente de trabajo es mejor. El mero hecho de tener, por ejemplo, filtros anti-polen en el sistema de aire acondicionado, hace que el aire dentro de una oficina sea más sano y se pueda trabajar mejor. O tener un vigilante en la puerta del edificio que evite la entrada a personas no deseadas también mejora sustancialmente el confort en el trabajo diario. Sin embargo, en muchas empresas todavía no se ha llegado a implementar un sistema de protección contra intrusiones informáticas adecuado. Sí es cierto que la protección contra códigos maliciosos es muy común, ya que los empresarios y responsables de sistemas se han dado cuenta de que la probabilidad de perder información por culpa de un código malicioso es muy alta si no de dispone de un sistema antivirus. De esta manera, la base de datos de clientes, los proyectos, las propuestas, en definitiva la base de funcionamiento de la empresa, estará a salvo.

Pero hay un nivel que todavía debemos alcanzar en los sistemas de protección. Las amenazas no se ciernen exclusivamente sobre los datos, sino que en los últimos tiempos se han lanzado en picado sobre el dinero. Cada vez más los códigos maliciosos se crean con el objetivo de obtener un beneficio económico, llegando al caso de cifrar ficheros, exigiendo una cantidad económica a cambio de la clave de descifrado. De todos es conocido también que el phishing supone un claro peligro para los usuarios. Este tipo de estafa consiste en mandar un correo electrónico a una colección de direcciones de email (al más puro estilo spam) en las que se informa de un supuesto problema con los datos de la cuenta bancaria por Internet del usuario. Evidentemente, no existe ningún problema real con la cuenta del usuario, pero aquel que pique puede verse en un serio problema, al haber dado el control de su cuenta bancaria a un estafador. En todos estos casos, una empresa podría desentenderse de estos problemas, ya que no son sino errores en los que cae el usuario, sin que tenga mayor repercusión para la marcha de la empresa. Pero vendría a equivaler a la instalación de un filtro anti-polen como el que mencionábamos antes. Un problema alérgico en un empleado no es problema que deba resolver la empresa, pero sin duda entra dentro de las obligaciones y responsabilidades de los empresarios mantener un aire lo más puro posible para sus empleados. Pues lo mismo ocurre en una instalación informática corporativa. Los empleados deben tener una protección que vaya más allá del simple filtrado de códigos maliciosos, ya que el malware va mucho más allá de lo que son los clásicos virus o troyanos. En el mejor de los casos, alguna empresa puede llegar a pensar que sus cuentas bancarias pueden estar amenazadas, pero el control interno que siempre hay en los departamentos de administración de las empresas convierten los ataques de phishing en prácticamente inútiles a nivel corporativo. No descartemos, sin embargo, http://digital.revistasprofesionales.com


CANAL PANDA

La seguridad IT, también para los empleados

que en el futuro puedan verse involucradas también las cuentas bancarias corporativas. Hoy en día la definición de malware no solamente incluye código ejecutable, como se podría suponer al derivarse su nombre de “malicious software”. Para que algo cause daño en una instalación informática no es necesario que sea software, puede ser un simple mensaje de correo electrónico. Nadie tiene la más mínima duda de que el spam es pernicioso, y sin embargo un email, por mucha Viagra que anuncie, no es software: es un mensaje de correo electrónico que se podrá leer de una manera u otra, pero en principio, no va a llevar a cabo una tarea dentro del sistema más allá de la molestia que supone su recepción y eliminación. Lo mismo ocurre con el phishing, no es código ejecutable, pero el peligro que entraña es elevadísimo y debe incluirse algún tipo de filtro anti-phishing integrado con los sistemas de protección corporativa. El uso de herramientas distintas dentro de los sistemas de seguridad para problemas distintos hace que se pierda la visión integrada de la protección, generando huecos muy difíciles de taponar. A todas estas amenazas hay que sumar una a la que no se le da la importancia suficiente en las empresas: el spyware. En muchísimas ocasiones se piensa que el spyware es un problema para los usuarios finales, para los usuarios domésticos. Estos mismos usuarios son los que están delante de los ordenadores en las empresas, y aunque la información que intentan robar estos códigos espías suele ser referente a hábitos de navegación, no debemos olvidar que el spyware suele incluir entre sus funciones la de grabación de pulsaciones de teclas (“keylogging”). Un listado de las pulsaciones de teclas en el ordenador de un trabajador quizá no consiga tarjetas de crédito, pero sí una ingente cantidad de datos que harán las delicias de los competidores. A pesar de las medidas clásicas de seguridad contra espionaje industrial, las empresas deben tener en cuenta que una teóricamente inocua barra de navegación instalada por un empleado (y no detectada adecuadamente por el sistema antimalware) puede dar a traste con un proyecto millo-

Panda Software lanza una nueva versión de Panda WebAdmin, la solución antimalware para empresas que cuentan con múltiples portátiles y/o delegaciones

Los clientes de WebAdmin pueden administrar la seguridad de sus equipos remotamente desde http://webadmin.pandasoftware.com

Panda WebAdmin Antivirus está disponible en español, inglés y japonés, e incluye los servicios de la gama corporativa de Panda Software: soporte telefónico o vía e-mail para la resolución rápida de cualquier incidencia que pueda surgir, actualizaciones diarias contra nuevos virus, SOS Virus 24h/365d para el envío de ficheros sospechosos e información proactiva (sobre los nuevos virus que aparecen, del estado vírico mundial, etc.). Asimismo, Panda Software ofrece a sus clientes, gratuitamente, todas las mejoras que vaya incorporando a esta solución. El lector interesado puede ampliar esta información en: http://empresas.pandasoftware.es/ productos/webadmin/

Panda Software ha lanzado recientemente una nueva versión de su solución antimalware con gestión remota vía Internet, Panda WebAdmin. La nueva versión de WebAdmin proporciona seguridad a las empresas no solamente frente a virus, gusanos y troyanos, sino también frente a otras amenazas de Internet como spyware, adware, dialers o hacking tools. Panda WebAdmin consta de un cliente antivirus instalado localmente en los equipos a proteger, así como de un innovador sistema de gestión centralizada alojado en los servidores de Panda Software (http://webadmin.pandasoftware.com), desde el cual el administrador puede instalar, configurar y monitorizar la seguridad de toda la empresa de forma remota. En Panda WebAdmin, el criterio fundamental para poder gestionar la seguridad es que los equipos estén conectados a Internet y no que estén conectados a la red corporativa. Esto permite al administrador controlar la seguridad en equipos remotos que no formen parte de la red corporativa, como son portátiles y ordenadores de delegaciones remotas, así como reducir los costes asociados al mantenimiento de la seguridad informática.

WebAdmin informa continuamente de la actividad vírica.

nario. O sin ser tan tremendistas, puede simplemente servir de “vía de escape” a direcciones de correo para engrosar las listas de los spammers. Si las empresas de hoy en día quieren establecer un sistema de seguridad efectivo, deben vigilar que todos los riesgos

(ataquen directamente a la empresa o bien a los empleados) estén vigilados y solucionados. La concepción global de la seguridad informática de hoy en día ofrece, y exige, soluciones completas para la seguridad completa.

El menú de administración de WebAdmin ofrece las funcionalidades necesarias para establecer las políticas de seguridad de la empresa.

Sobre el autor Fernando de la Cuadra (Fdelacuadra@pandasoftware.com) es editor técnico internacional de Panda Software (http://www.pandasoftware.com).

http://digital.revistasprofesionales.com

11

SOLO PROGRAMADORES nº 127


ACTUALIDAD

IPv6: Oportunidad para la innovación, oportunidad para nuevos negocios El IGC2005 da la bienvenida a IPv6 El Internet Global Congress (IGC) celebró en Barcelona su séptima edición del 6 al 10 de junio de 2005 consolidándose como el evento de referencia sobre tecnología, innovación y sociedad digital en España. Entre los temas tratados, destacó por encima de todos el presente y futuro de IPv6. Por eso reproducimos aquí el análisis de Jordi Palet Martínez, donde el autor expone los problemas de la Internet actual, y qué posibilidades puede abrir IPv6 en el futuro.

SOLO PROGRAMADORES nº 127

12

JORDI PALET MARTÍNEZ (CEO/CTO, Consulintel. Presidente del Grupo de Trabajo de Educación, Promoción y Relaciones Públicas de IPv6)

En estos últimos años, hemos sido espectadores de un crecimiento importante y continuado de Internet. Sin embargo, este crecimiento ha tenido únicamente dos “golpes de efecto” destacables desde el punto de vista del usuario final: el correo electrónico y la web. Por supuesto que han aparecido múltiples aplicaciones y servicios, pero en realidad me atrevería a decir que no son tantos, si tenemos en cuenta que la mayoría de éstos, de una manera o de otra, se basan en los dos anteriores (o en los protocolos sobre los que se soportan). Por lo tanto, estamos legitimados para afirmar que la innovación en Internet prácticamente se ha detenido. Debido a que hoy dependemos en gran medida de Internet para cualquier tipo de aplicaciones y servicios, ya sea en el lugar de trabajo o en el hogar (servicios de ocio, información, etc.), y dado que existe una evidente ausencia de innovación en Internet, en cierto modo podemos afirmar que esto repercute negativamente en el desarrollo de la sociedad de la información y del conocimiento. Hay un motivo fundamental para que se produzca esta falta de innovación en Internet: la dificultad de desarrollar aplicaciones que funcionen extremo a extremo, en cualquier lugar

Jordi Palet Martínez es CEO/CTO de Consulintel y fue ponente en el IGC2005.

de la red, y que además puedan ser seguras y puedan funcionar en movimiento (la cual cosa tiende a ser cada vez más habitual en entornos tanto de trabajo como de ocio). Esta dificultad se debe al uso casi universal de NAT. Hay centenares de aplicaciones y desarrollos que residen en los laboratorios por falta de recursos de sus creadores, porque al intentar ponerlos en el mercado, se encuentran que los escenarios de redes son tan diversos como las posibles combinaciones de fabricantes y modelos de NAT. La forma en la que se construye NAT no está concretada en ningún estándar, lo cual impide definir un mecanismo único que permita “abrir” un camino a través de NAT cuando una aplicación así lo requiera. Aún definiendo http://digital.revistasprofesionales.com


ACTUALIDAD

IPv6: Oportunidad para la innovación, oportunidad para nuevos negocios

ahora este estándar, ¿Cuánto tiempo tardaríamos en actualizar todos los NAT que hay en la red, teniendo en cuenta que hay millones de ellos? ¿Qué coste tendría? Sin lugar a dudas, puedo asegurar que tanto el coste como el tiempo serían muy superiores y mucho menos eficaces que la inversión requerida para hacer la transición a IPv6, que por otra parte aporta multitud de ventajas adicionales y, especialmente, la posibilidad de extensión de todo aquello que pueda hacer falta en un futuro, prácticamente sin límites. En estos momentos, el mercado ya ha reconocido las ventajas de IPv6, aunque para ser sinceros ha costado un poco más de lo que se esperaba en un principio. En los últimos dos años, aproximadamente, el interés por IPv6 ha sido exponencialmente creciente, y su despliegue está avanzando a pasos agigantados, con noticias interesantes prácticamente cada día des de hace más de seis meses acerca de nuevas implantaciones en redes, semiconductores, dispositivos y, en general, todo tipo de productos, aplicaciones y servicios. Además, este interés no ha sido exclusivo por parte de proveedores de servicios de Internet, sino que también ha llegado a la industria en general y, especialmente, a fabricantes de productos de consumo donde, sin duda, pronto descubriremos novedades impensables hasta ahora (domótica, audio/vídeo/multimedia, oficina, televigilancia, etc.). Veamos un ejemplo. Una de las aplicaciones que hoy en día está teniendo más éxito es la voz sobre IP, y un caso concreto es el éxito de Skype, una creación reciente de los diseñadores de KaZaA. Esta aplicación ha requerido y requiere el 80% de la inversión de la compañía, des de que ésta empezó a desarrollarse, con el objetivo de asegurar que la aplicación funcionara en cualquier lugar de la red. Evidentemente, los propietarios de Skype se lo pueden permitir, ya que con la venta de su desarrollo anterior, KaZaA, consiguió recursos financieros abundantes. Aún así, sería interesante ver cuánta creatividad adicional podrían desarrollar sus ingenieros, ya sea en mejoras sobre el mismo Skype, ya sea en otras aplicahttp://digital.revistasprofesionales.com

ciones, con este 80% de los recursos. Pero, sin duda, este no es el caso de la mayoría de las pequeñas y medianas empresas, ni de la comunidad de desarrolladores de código abierto, ni de desarrolladores independientes. Éstos no suelen disponer de este 80% de recursos para malgastar haciendo una herramienta que solucione un problema que debería estar resuelto y garantizado por la red. Se puede afirmar categóricamente que desarrollar para redes IPv4 supone más costos, más complejidad técnica, y plazos

Es equiparable al caso de los servicios de televisión por cable o satélite. El cliente paga una cuota básica mensual para la suscripción al servicio (lo que equivaldría a la cuota de conexión a la red de banda ancha en nuestro caso). Aún así, el cliente generalmente contrata paquetes de canales temáticos adicionales como cine, infantiles, documentales, etc., o hasta de vez en cuando, servicios puntuales (pay per view). No es necesario que los ISPs desplieguen soporte nativo de IPv6, no inicialmente, sino que han de ser capaces de activar en

http://www.consulintel.es

de desarrollo y mantenimiento muy superiores, la cual cosa muchas veces provoca el abandono de las ideas, e impide la innovación. Me atrevería a predecir un nuevo “boom” de Internet cuando haya una masa crítica de desarrolladores que exploten el potencial que les ofrece IPv6. ¿Dónde está el negocio, con IPv6, para los ISPs? Por un lado, hay que tener en cuenta que el servicio de acceso está dejando de ser un negocio; competencia, más requisitos de ancho de banda y de calidad de servicio, conllevan reducciones de márgenes. Por otro lado, la demanda de más ancho de banda no está compensada con la prestación de nuevos servicios, con el despliegue de nuevas aplicaciones, y es aquí donde se produce la oportunidad de negocio. Los ISPs son el canal perfecto para llegar a millones de clientes, con unos costos de despliegue mínimos, para que cualquier emprendedor pequeño pueda comercializar sus aplicaciones. IPv6 es la oportunidad para que los ISPs se puedan convertir en “aglutinadores de servicios” e incrementen sus volúmenes de facturación, sus márgenes y sus servicios a los clientes, con una amplia oferta de aplicaciones.

sus redes mecanismos de transición que aprovechen el potencial de transportar IPv6 a las redes IPv4 existentes. Con esto pueden reducirse prácticamente a cero las inversiones iniciales. De esta manera, el soporte nativo a IPv6, y también la desaparición de IPv4 en las redes troncales y de acceso, serán cambios progresivos, cuando se hayan conseguido volúmenes de negocio que justifiquen este cambio. Por otro lado, en el momento en el que estén disponibles más aplicaciones y servicios, los usuarios tendirán a requerir más ancho de banda, más calidad de servicio, menos retrasos, etc. Servicios que, lógicamente, generarán ingresos adicionales. Todo esto, sin tener en cuenta que no aprovechar esta oportunidad es dar pie para que la competencia se anticipe, con el riesgo consiguiente de perder clientes. El nuevo negocio de los ISPs es, por lo tanto, sacar partido a las redes de banda ancha con IPv4 para desplegar aplicaciones y servicios que aprovechen las capacidades extremos a extremo de IPv6. El nuevo negocio empieza por “regalar” las direcciones IPv6 a los clientes, y facilitar así que exploten el desarrollo de estas aplicaciones y servicios: la innovación. 13

SOLO PROGRAMADORES nº 127


ACTUALIDAD

Java Expo 2005 CARLOS LAPARRA

El pasado 1 de Junio tuvo lugar, en Madrid, la VIII edición de Java Expo, un foro que contó con la participación de más de 40 empresas y con la asistencia de más de 3000 profesionales. Un repaso a Java Expo A lo largo de más de 80 ponencias, esta edición de Java Expo ha servido para exponer puntos de debate de máxima actualidad en ámbitos como el desarrollo de software en comunidad, los nuevos modelos de servicios financieros, las últimas propuestas en el entorno de los servicios online de la Administración Pública española, la productividad de la distribución y el impacto de la cadena de valor, los nuevos servicios asociados a la evolución de las operadoras de telecomunicaciones o el futuro tecnológico del transporte inteligente en España. Java Expo 2005 ha cobrado este año un carácter especial al coincidir con el 10º aniversario del nacimiento de la tecnología Java. Con este argumento, y también para dar prestigio al evento, James Gosling, vicepresidente de Sun Microsystems y reconocido a nivel internacional como el “padre de Java”, fue quien hizo la intervención inaugural del congreso. Durante dicha intervención, hizo un repaso de la evolución de Java. Gosling, quien lideró el denominado “Green Team”, encargado de

desarrollar el lenguaje de programación que en 1995 sería lanzado comercialmente con el nombre de Java, manifestó que: “Estoy realmente orgulloso de la dimensión que ha alcanzado el fenómeno Java. En estos diez años, esta tecnología ha logrado revolucionar áreas que afectan a partes muy importantes de nuestra vida, como puede ser el cuidado de la salud. Y el futuro de Java estará en cualquier lugar donde haya un dispositivo digital conectado a la red al que se pueda añadir valor”.

Demostraciones prácticas Java Expo 2005 ha congregado a un amplio espectro de profesionales con capacidad de decisión de empresas de todos los tamaños, así como a los profesionales técnicos interesados en la aplicación de soluciones basadas en Java. A parte de las sesiones plenarias, conferencias paralelas y experiencias de éxito basadas en Java, se habilitó una zona de exposición en la que los asistentes pudieron conocer, de primera mano, las soluciones propuestas por Sun y su comunidad iForce de partners tecnológicos de valor añadido. Entre otras muchas demostraciones, se presenció una ilustración práctica sobre Liberty, una especificación abierta que garantiza la identidad en red y permite un inicio de sesión único, seguro y ubicuo para múltiples sitios web y múltiples dispositivos conectados a Internet.

Conclusiones

Más de 3000 profesionales se dieron cita en la VIII edición de Java Expo.

SOLO PROGRAMADORES nº 127

14

La VIII edición de Java Expo ha dejado patente que en esta última década, gracias a su versatilidad, eficiencia, portabilidad y seguridad, Java se ha convertido en una opción muy importante para el desarrollo y despliegue de aplicaciones y servicios para la red, y ha generado a su alrededor un ecosistema que crea cada vez mayores oportunidades de negocio y valor añadido.

http://digital.revistasprofesionales.com


ACTUALIDAD

Inteligencia Ambiental: la presencia invisible IÑAKI VÁZQUEZ y DIEGO LZ. DE IPIÑA GZ. DE ARTAZA (Facultad de Ingeniería (ESIDE) - Universidad de Deusto)

¿Cuánto tiempo malgastamos diariamente en hacer comprender nuestras intenciones a los dispositivos y servicios de alrededor una y otra vez? ¿Puede el sistema de iluminación de mi vivienda percibir por sí mismo si necesito luz o no para leer o cocinar, y activarla si es necesario? Si mi vecino siempre sabe a qué piso voy, ¿por qué no lo sabe el ascensor que uso todos los días? Existe un futuro donde todo esto sí es posible, y llega de la mano de la Inteligencia Ambiental.

cuento de hadas los objetos dejan de estar inanimados y reaccionan por iniciativa propia para facilitarnos la vida: puertas de supermercado que se abren a nuestro paso o grifos que proporcionan agua automáticamente cuando nos vamos a lavar las manos; estamos hablando de un futuro que en cierta medida vive con nosotros desde hace algún tiempo y que cada vez está más presente. Antes de seguir hay que señalar que en este marco la palabra “ambiente” no debe interpretarse como “medioambiente”, sino como “entorno”, y así, “inteligencia ambiental” se interpreta como “inteligencia en el entorno”.

Componentes tecnológicos Desde un plano más científico, vamos a analizar las bases tecnológicas que subyacen tras el concepto de Inteligencia Ambiental. Los cuatro atributos fundamentales que caracterizan un sistema AmI son:

Introducción 

La Inteligencia Ambiental no se siente, no se ve, no se toca, pero actúa constantemente, como una presencia invisible SOLO PROGRAMADORES nº 127

16

La Inteligencia Ambiental (AmI – Ambient Intelligence) es un modelo de interacción en el que las personas estamos rodeadas de un entorno digital consciente de nuestra presencia, sensible al contexto, que responde de manera adaptativa a nuestras necesidades y hábitos, para facilitarnos la vida diaria en el hogar, lugares de ocio y trabajo. Existe inteligencia en el ambiente, y es invisible. Es una especie de ángel de la guarda, que nos acompaña y nos ayuda, nos abre las puertas, enciende las luces, nos indica que hay otro programa más interesante en el televisor y paga nuestro autobús. ¿Cuánto tiempo invertimos al día en manipular los elementos del entorno para configurarlos y adaptarlos a nuestras necesidades en cada momento? ¿Se podrían crear ambientes en los que la interacción activa del usuario sea mínima, entornos que perciban las situaciones y respondan de la manera adecuada? El concepto de Inteligencia Ambiental dispone el mundo al servicio de las personas. Como en un



C o m p u t a c i ó n , c o m u n i c a c i ó n e i n f o rm a c i ó n u b i c u a: un usuario estará rodeado de pequeñas computadoras en su entorno, que se comunican y gestionan la información necesaria para adaptarse a las necesidades de la persona. El televisor, el control de luces y de calefacción, el sistema de control de acceso de la puerta o el salpicadero del vehículo son algunos de los ejemplos de estos objetos avanzados, representando una capacidad de computación y comunicación por doquier, vigilante, para reaccionar a la conducta del usuario de manera inteligente. S e n s i b i l i d a d a l c o n t e x t o: el entorno es sensible a su circunstancia y a los elementos que la determinan. Esta sensibilidad se implementa mediante sensores, que permiten percibir el entorno. No hay límites para la naturaleza de dichos sensores, desde los más primitivos como los sensores de temperatura, de luz o de presencia, hasta los más sofisticados como microcámaras, o elementos software reconocedores de patrones de conducta, que correlacionan las entradas de los demás para http://digital.revistasprofesionales.com


ACTUALIDAD

Inteligencia Ambiental: la presencia invisible

Figura 1. Ejes de desarrollo de la Inteligencia Ambiental.





identificar la situación o tarea en la que está inmerso el usuario. Incluso las tecnologías de comunicación como Wi-Fi, Bluetooth o RFID se pueden utilizar para percibir la presencia de otra entidad, como si de un sexto sentido se tratase. I n t e l i g e n c i a : este entorno digital dotado de capacidades de computación y percepción es capaz de razonar sobre los estímulos que detecta y reaccionar de la manera que estima conveniente para adaptarse a las necesidades del usuario. El sistema de razonamiento puede estar localizado en uno de los dispositivos, que coordina a los demás, o más bien, surgir de manera espontánea por la aportación individual y la cooperación entre éstos. Por ejemplo, un profesor llega a su despacho, el sistema de control de accesos de la puerta le identifica a través del PDA y notifica de ello al resto de dispositivos, provocando que el ordenador realice un login automático, y que la temperatura y luz se ajusten automáticamente a sus preferencias. I n t e r a c c i ó n n a t u r a l: el usuario no tiene porqué ser un agente completamente pasivo en este entorno, sino que puede interactuar explícitamente

http://digital.revistasprofesionales.com

mediante mecanismos de comunicación similares a los que desarrolla de manera natural, como la voz o los gestos. De este modo, nuestro profesor podría variar la temperatura del despacho, bloquear la puerta o apagar las luces solicitándolo al entorno. En definitiva, estamos hablando de ambientes con una cierta capacidad de computación y comunicación, sensores, procesamiento inteligente para la toma de decisiones, que pueden interactuar con el usuario de manera natural. Como ya hemos comentado, la idea que se esconde detrás de AmI es descargar a las personas de la tarea de reconfigurar continuamente el entorno que nos rodea debido al cambio de ciertas condiciones, la presencia de nuevas entidades, el desarrollo de otras actividades, etc. Es precisamente la Inteligencia Ambiental presente en el medio la que se encarga que ejecutar y coordinar todas las acciones necesarias para establecer el hábitat adecuado en el que pueda desenvolverse nuestra conducta. El paradigma de la Inteligencia Ambiental se puede analizar en dos ejes diferentes, propuestos por el ISTAG (Information

Society Technology Advisory Group, Grupo de Asesoramiento en IST de la Comisión Europea), y del que la figura 1 es una adaptación. Por una parte tenemos el eje horizontal, que indica la proximidad de los conceptos o componentes de una visión centrada en la tecnología a una visión centrada en la persona. Por otra parte, el eje vertical señala la naturaleza relacional del componente, su aislamiento o capacidad de convergencia e integración con otros. Los componentes tecnológicos que pueden ser aplicados en AmI se han dividido en tres grandes bloques. El primero, “Ambiente”, contiene los elementos particulares de componente fundamentalmente tecnológica que constituyen los bloques básicos sobre los que edificar un escenario de Inteligencia Ambiental. El segundo bloque, “Inteligente”, contiene los elementos particulares más próximos a prestar servicios a la persona, que le proporcionan un mayor valor añadido o interactúan directamente con ella. El tercer bloque, en la parte superior del gráfico, es el verdadero desafío de AmI. Su misión es aportar la integración entre todos estos elementos individuales para coordi17

SOLO PROGRAMADORES nº 127


ACTUALIDAD

y progresivamente ya se están introduciendo pequeñas mejoras en nuestra vida diaria, casi imperceptibles, que implementan el verdadero concepto que subyace aquí. Ya no nos sorprenden las puertas que se abren ante nuestro paso, las luces que se encienden solas, o los sistemas de climatización que se autoregulan según la presencia o no de personas en la sala. Esta es la esencia de la Inteligencia Ambiental: no se siente, no se ve, no se toca, pero actúa constantemente, como una presencia invisible.

Un escenario, a modo de ejemplo

Figura 2. En AmI el concepto central es la persona, no la tecnología que la sirve.

nar su comportamiento, de tal manera que constituye el sustrato del entorno inteligente. Es aquí donde se ubican las arquitecturas tecnológicas y los estándares que permiten un entorno dinámico, de tipo Plug and Play, donde emerge una percepción ambiental constante. Los dispositivos se descubren unos a otros, se informan mutuamente de las percepciones individuales, se toman decisiones coordinadas y se ejecutan las adaptaciones individuales o colectivas que sean necesarias.

El centro es la persona Lo que diferencia el concepto europeo de AmI respecto a otros similares como pervasive computing o sentient computing, es su manifiesta orientación, casi violenta, a tomar a la persona como centro del universo: que todos los ingenios, sistemas y servicios giren alrededor de él para extender y mejorar la propia experiencia vital en cualquier ámbito. Parafraseando al ISTAG, “la Inteligencia Ambiental debe estar conducida desde una perspectiva humanística, no tecnológica, y debe ser controlable por gente corriente […]. Debe promover y facilitar la participación del individuo en la sociedad y en sus

SOLO PROGRAMADORES nº 127

18

diferentes comunidades, en todos los aspectos de la vida, del entretenimiento al gobierno”. Así, finalmente, la Inteligencia Ambiental, no sólo consiste en un elevado desarrollo tecnológico en múltiples campos, que permita facilitar la relación de las personas con su entorno de manera transparente, sino que constituye una oportunidad para mejorar y modernizar diversos aspectos del modelo social. El gap existente entre los desafíos sociales y la tecnología se cubre mediante el concepto de AmI Space: un escenario de aplicación para un determinado tipo de situaciones, donde la tecnología está al servicio de la sociedad para modelar un entorno inteligente en el que puedan desenvolverse las personas. Hay diferentes AmI Spaces definidos para ámbitos paradigmáticos: el hogar, el lugar de trabajo, el vehículo, lugares de recreo, etc. En cada uno de estos entornos las personas se comportan de modo diferente, las tecnologías y la inteligencia invisible que reside en el ambiente se enfocan de maneras distintas. Los desafíos más ambiciosos que propone AmI no se espera que sean alcanzados hasta el año 2020, pero desde el día de hoy

Para evaluar el impacto en nuestra vida diaria derivado de la aplicación de mecanismos de Inteligencia Ambiental, proponemos un experimento. Vamos a narrar el comportamiento y las acciones que lleva a cabo una persona mientras se dirige de casa al trabajo, y cómo algunas de dichas acciones se podrían automatizar con el empleo progresivo de mecanismos de Inteligencia Ambiental (véase el cuadro adjunto). El resultado es claro: sin AmI el usuario debe realizar 14 acciones activas de manipulación del entorno, mientras que aplicando mecanismos de Inteligencia Ambiental, se podrían reducir a 5 acciones en las que sólo se requiere su confirmación por motivos de seguridad.

Figura 3. Los AmI Spaces son la aplicación de las tecnologías en ámbitos concretos de la vida.

http://digital.revistasprofesionales.com


ACTUALIDAD

Inteligencia Ambiental: la presencia invisible

Acción sin AmI

Acción con Aml

Posible mecanismo Aml aplicado

Al salir de casa, cierro la puerta con llave...

Al salir de casa, la puerta se bloquea automáticamente

El sistema de control del hogar, al detectar que no queda nadie en casa, decide bloquear la puerta. Puede solicitar confirmación al usuario

...y llamo al ascensor

El ascensor acude a recogerme

El sistema de presencia de la escalera, quizá consultando un registro histórico, determina que el 98% de las veces que un usuario se encuentra a esta hora en este piso, llama al ascensor

Me dirijo al garaje, pulso el mando de apertura para elevar la puerta

Me dirijo al garaje, cuya puerta se abre ante mi presencia

Mi PDA se valida en la entrada cuando me aproximo, lo que me franquea el acceso. Puede solicitar confirmación al usuario

Abro el vehículo y entro

El vehículo se abre de manera automática

Mi PDA sabe que hoy es día laboral (gestiona mi calendario) y por lo tanto determina que voy al garaje a por el coche, e indica al vehículo que se abra. Puede solicitar confirmación al usuario

Inserto la llave y...

... me identifica...

El vehículo identifica al poseedor de la PDA (token de identificación) o mendiante otros mecanismos pasivos (huella dactilar al tomar el volante, ...)

...lo pongo en marcha Vuelvo a abrir la puerta del garaje para salir Sintonizo la emisora de noticias mientras conduzco al trabajo Busco una plaza libre durante cinco minutos y aparco el vehículo

...poniéndose en marcha...

El vehículo se pone en marcha o la PDA se lo indica

... y abriendo la puerta del garaje a mi paso El sistema de radio sintoniza la emisora que suelo escuchar a esa hora El vehículo me indica la ruta óptima y cómo llegar a una plaza libre

El vehículo o la PDA abren la puerta del garaje

Entro en mi edificio y me valido con la tarjeta en la entrada Indico mi piso en el ascensor

El sistema de control de accesos de edificio me franquea la entrada El ascensor me recoge y me lleva a mi planta

Uso la llave de la puerta del despacho para abrirlo

La puerta del despacho me permite el paso

Enciendo la luz del del despacho Enciendo mi ordenador y me valido

La luz se activa si es necesario El ordenador se inicia con mi sesión, quizá previa confirmación

: el usuario es el sujeto activo

: el entorno es el sujeto activo

Conclusiones Pero lo más difícil aún está por hacer. El diseño de arquitecturas inteligentes que permitan a los dispositivos encontrarse, conocerse y negociar entre sí a nivel semántico, sistemas que perciban con un mayor grado de certeza las intenciones de un usuario para adaptar el entorno a las tareas que desarrolla, interfaces naturales, gestuales o vocales, que permitan que las personas se comuniquen de manera no artificial con los servicios a su alrededor y que la interpretación sea correcta, ... son sólo algunos ejemplos de las áreas de trahttp://digital.revistasprofesionales.com

El sistema de radio conoce mis preferencias y las activa, o bien mi PDA se lo indica La PDA indica el destino al sistema de navegación, que busca la ruta óptima Mi PDA, tarjeta o tag verifican mi identidad por mí Esta identidad sirve para determinar mi destino en el ascensor, aunque siempre puedo variarlo El mismo mecanismo de identificación me valida en la puerta del despacho. Puede solicitar confirmación al usuario El sistema de presencia del despacho determina que es necesario activar la iluminación El mismo mecanismo de identificación me valida en la sesión del ordenador. Puede solicitar confirmación al usuario : el entorno es el sujeto activo, solicitando confirmación explícita del usuario

bajo en las que se hallan inmersos muchos equipos de investigación internacionales. Cuidado con los entusiasmos. El trabajo bien hecho es firme, pero se desarrolla lentamente. Que la especulación no nos conduzca a decepciones fácilmente evitables, como ocurrió con WAP o UMTS. Cada cosa a su tiempo. Mantengamos las expectativas de la Inteligencia Ambiental equilibradas desde el principio, que avance firmemente, cada vez con más implantación en nuestros ámbitos de vida: trabajo, vehículo, hogar..., y así, sin que lo notemos, nuestro entorno será más inteligente, más receptivo, más reactivo y más humano.

Referencias ● IST

Advisory Group. Ambient Intelligence: from vision to reality. EU Publication. 2003. ● IST Advisory Group. IST Research Content. EU Publication. 2003. ● IST Advisory Group. Scenarios for Ambient Intelligence in 2010. EU Publication. 2001. ● López de Ipiña, D. y Katsiri, E. An ECA RuleMatching Service for Simpler Development of Reactive Applications. IEEE Distributed Systems Online, vol. 2, number 7. 2001. ● Vázquez Gómez, J.I. y López de Ipiña, D. An Interaction Model for Passively Influencing the Environment. Adjunct Proceedings of the 2nd European Symposium on Ambient Intelligence. Eindhoven, The Netherlands. November 2004.

19

SOLO PROGRAMADORES nº 127


DISPOSITIVOS MÓVILES

Creación de un sistema de distribución de Midlets (I) JOSÉ ANTONIO PÉREZ

Una vez se tiene desarrollada una suite de Midlets la primera cuestión que nos puede venir a la cabeza es… ¿Cómo conseguir distribuirla de forma que llegue al máximo número de destinatarios? Introducción

En esta serie de dos entregas desarrollaremos un servicio de distribución de aplicaciones para teléfonos móviles según el modelo OTA y basado en IIS SOLO PROGRAMADORES nº 127

20

De todas las opciones para descargar aplicaciones en un móvil, es la distribución de aplicaciones Over The Air, comúnmente conocida como OTA, la que nos abre mayores posibilidades. Con este artículo iniciamos una serie en la que veremos cómo implantar un sistema de distribución de Midlets basado en el Internet Information Server, de aquí en adelante IIS. El desarrollo de un sistema OTA nos va a permitir difundir nuestras aplicaciones basadas en J2ME desde Internet. Así pues daremos las base teóricas de la programación de una extensión IIS y se explicarán los conceptos y elementos que están detrás de OTA. En una primera parte se hará un repaso a los conceptos de HTTP en los que se basa la distribución Over The Air, necesarios para entender el funcionamiento de nuestro aplicativo. Se explicará cómo crear una extensión para IIS y la diferencia con otras tecnologías afines de Microsoft, así como las APIs implicadas. Aunque se podía haber optado por una solución basada en Servlets o en módulos Apache, se han descartado ambas en favor de una solución basada en IIS por varios motivos. En el caso de los Servlets se hubiera aportado información que no resultaría novedosa, dado que la implementación de un Servlet es realmente trivial. En el caso de escribir un módulo Apache aún no siendo obvia su creación, se nos planteaba el problema de decidir qué versión usar (1.x o 2.x) así cómo explicar toda la temática de la gestión de los módulos y filtros en Apache. En todos los casos (Servlet, módulos, extensiones) nos basta con conocer poco más de media docena de funciones para implementar las funcionalidades básicas necesarias.

Se ha optado por una implementación en IIS porque aún no teniendo una ventaja técnica y de productividad sobre las otras opciones, nos servirá para tratar otros temas al margen de OTA. La aplicación desarrollada podrá ser ejecutada en una variedad de sistemas operativos como Windows 2000 Server, Windows 2003 Web Edition (IIS 6.0) e incluso Windows XP Profesional (IIS 5.1). Una cuestión a tener en cuenta es que la versión de IIS que se entrega con Windows XP Profesional no está pensada para un uso intensivo en web, de hecho el número máximo de conexiones simultáneas que puede mantener nunca debe exceder la decena. Dadas las características de la propia aplicación puede ser más que suficiente en la mayoría de los casos, pero se recuerda que si se quiere implantar en un entorno de explotación con requisitos que excedan lo personal se tendrá que recurrir a las licencias más caras de las versiones servidor. Una observación más a tener en cuenta, es que cuando desarrollemos extensiones deberemos tener presentes las dependencias con otras DLL, dado que pueden llevarnos a errores de funcionamiento si éstas no están disponibles en el sistema operativo destino. Para verificar las dependencias podemos recurrir a herramientas como tdump de Borland (con la opción –em) o dumpbin de Microsoft (con la opción /IMPORTS). Finalmente decir que la aplicación se ha escrito en C++ usando Visual Studio .NET 2003, aunque es muy fácil portar el proyecto a otros compiladores existentes.

Conceptos básicos de HTTP HTTP esta pensado para el tratamiento de información distribuida cuyo máximo exponente es el World Wide Web. Para localizar los recursos en la web se usan las URL. A continuación repasaremos cuáles son y cómo designaremos a los elementos que componen un Uniform Resource Locator. Una URL tiene la siguiente estructura: [PROTOCOLO][HOST][PUERTO][VIA DE ACCESO][CONSULTA]

http://digital.revistasprofesionales.com


DISPOSITIVOS MÓVILES

Creación de un sistema de distribución de Midlets (I)

En un recurso HTTP la URL tendría el siguiente formato de forma más específica: http://host[:puerto] [vía_de_acceso[?consulta]]

Sólo los elementos protocolo y host son obligatorios. La consulta o query nos permite añadir parámetros a la petición para ayudar a producir contenidos de forma dinámica. A diferencia de otros protocolos como el FTP en el que existe una sesión a nivel de conexión entre el cliente y el servidor, el HTTP es un protocolo sin estado (stateless). En HTTP el cliente establece conexiones puntuales con el servidor. Estas conexiones finalizan con la respuesta del servidor, sin pasar por estados como login, queries y logout. Las conexiones HTTP suele hacerse en el puerto 80 (puerto por omisión). Otro puerto usado por convenio es el 8080. Los datos que un cliente envía a un servidor en una conexión HTTP y los datos que se responden desde un servidor a un cliente se dividen en dos partes: las cabeceras (HEADER) y el cuerpo (ENTITY). Las cabeceras están formadas por una sucesión de líneas de texto separadas por el par de caracteres “\r\n”. La primera línea de las cabeceras que envía un cliente a un servidor se usa para indicar el método de conexión y el recurso afectado. El formato de esta línea es: <Método de conexión><espacio><URI> <espacio><Versión de HTTP><”\r\n”>

El “<Método de conexión>” o verbo, es una palabra reservada que indica el tipo de conexión deseada por el cliente (GET, POST...). El “<URI>” indica cómo identificar el recurso o fuente a solicitar o interpelar en el servidor. La “<Versión de HTTP>” indica la versión soportada por el cliente y es únicamente el texto “HTTP/” seguido del número de versión y subversión separada por un punto. Dos ejemplos de esta sintaxis podrían ser: POST /midlets/midsvr.dll HTTP/1.1 GET /midlets/admin.htm HTTP/1.1

A continuación de esta línea obligatoria para todas las peticiones HTTP aparecen una sucesión de cabeceras que designaremos con el nombre de atributos, y sus valohttp://digital.revistasprofesionales.com

Campos de las cabeceras HTTP más usuales Nombre del campo o atributo Accept Accept-Encoding

Descripción del campo

Cliente

Indica los mime types de los formatos de respuesta aceptados. Tipo de codificación aceptada (gzip, deflate, compress...).

Servidor

X X

Accpept-Language

Lenguaje preferencia para la recepción de la respuesta.

X

Cache-Control

Define el uso que hace de la cache.

X

Connection

Especifica el tipo de conexión.

X

X

Content-Encoding

Indica qué codificación se aplica al ENTITY.

X

X

Content-Length

Longitud total del ENTITY.

X

X

X

X

X

X

Content-Type Date Host

El tipo de contenido del ENTITY (mime/type). Fecha y hora en la que se generó el mensaje. Host y número de puerto del recurso solicidado.

X

Location

Usado para redirigir el recipiente a otra dirección.

X

Server

Contiene información acerca del software del servidor.

X

User-Agent

Contiene información acerca del agente que creó la petición.

res correspondientes. Cada atributo/valor viene separado del siguiente por el par de caracteres “\r\n”. Los atributos pueden ser especificados en cualquier orden y no todos los atributos son necesarios para una petición. El formato de las líneas que contienen los atributos es: <Nombre del atributo> <el carácter:><espacio> <valor del atributo><”\r\n”>

En el cuadro “Campos de las cabeceras HTTP más usuales” se describen los atributos más corrientes. La lista de atributos finaliza con una línea vacía (únicamente contiene el par de caracteres “\r\n”) viniendo a continuación (sí existe) el cuerpo de mensaje o ENTITY. Las respuestas del servidor tienen la misma estructura que una petición HTTP de cliente aunque difieren en algunas cuestiones. La primera línea del área de cabecera (línea de estado) sirve para retornar el código de estado de la petición y tiene la siguiente estructura:

X

<Versión de HTTP> <Código numérico de estado> <espacio> <descripción del código de la operación> <”\r\n”>

Donde “<Versión de HTTP>” es únicamente el texto “HTTP/” seguido del número de versión y subversión separada por un punto. Los códigos de estado reportados por un servidor HTTP están categorizados en 5 grupos dependiendo del ámbito de los mismos:  1 x x: Códigos informativos.  2 x x: Códigos de operación finalizada con éxito.  3 x x: Códigos de redirección.  4 x x: Códigos de error cuya causa está en la petición cliente.  5 x x: Códigos de error generados por el servidor.

Los métodos de conexión El protocolo HTTP define 8 métodos de conexión. Veamos cada uno en detalle:  O P T I O N S : representa una petición sobre las opciones de comunicación. 21

SOLO PROGRAMADORES nº 127


DISPOSITIVOS MÓVILES



T R A C E: permite al cliente ver qué es lo que se está recibiendo al final de una petición y usar estos datos para test o diagnóstico.  C O N N E C T: se usa para realizar conexiones a un servidor a través de un proxy. Los métodos de conexión más frecuentes usados por los navegadores son “GET”, “POST” y “CONNECT”.

ASP, extensiones y filtros Figura 1. Una petición HTTP puede servirnos par enviar archivos binarios al servidor usando como encoding el tipo “multipart/form-data”. 





 

G E T: Se pide unos datos que se reciben como parte de la respuesta del servidor. Es el método usado en las peticiones que se realizan cuando se introduce una URL en un navegador de Internet. La URI contiene todos los parámetros que pueden ser necesarios para la petición. H E A D: es idéntico al método “GET” pero el servidor no retorna el cuerpo de la respuesta. Sólo se envían las cabeceras. P O S T: Se usa para pedir al servidor que acepte las “entities” adjuntadas como elementos de la petición a realizar. Es el método usado típicamente para enviar formularios cuyos datos aparecen en el cuerpo como parejas atributo-valores separados por caracteres “&”. Caso especial lo constituye el envío de varias entidades en un mismo formulario para enviar, por ejemplo, archivos al servidor, (“FORMS” en los que se define el atributo enctype=”multipart/form-data”). En este caso se define un “boundary” para separar los diferentes “entities” (véase un ejemplo en la figura 1). P U T: se pide que la “entity” adjunta se guarde como la URI indicada. D E L E T E: se pide al servidor que borre un recurso identificado por la URI de la petición.

Figura 2. Para poder administrar la extensión deberemos identificarnos en primer lugar.

SOLO PROGRAMADORES nº 127

22

El IIS permite que el desarrollador pueda gestionar las peticiones web despreocupándose de lo relativo a la conexión y centrándose en la funcionalidad. Para ello proporciona tres mecanismos: páginas ASP (ASP.NET), filtros y extensiones. Las páginas ASP y ASP.NET comparten gran similitud en cuanto a la funcionalidad y la finalidad de las páginas JSP, PHP, o ColdFusion. Todas ellas son tecnologías que permiten crear páginas dinámicas en el servidor. Las páginas ASP permiten usar objetos COM de Windows para crear las páginas HTML que se envían al lado cliente. El código de las páginas ASP puede incluir tags HTML y código escrito en VBScript o JScript. ASP.NET es el resultado de la evolución de las tecnologías de Microsoft orientado a la creación de contenidos dinámicos de la web. ASP.NET hace posible que se use cualquier lenguaje de programación con soporte para .NET. Además, al igual que ocurre en otras tecnologías como los JSP, el código se compila la primera vez que se invoca una página mejorando el rendimiento del servidor. El interfaz de programación de aplicaciones para el servidor de aplicaciones de Internet (ISAPI) proporciona un conjunto de interfaces para crear tanto extensiones como filtros para el Internet Information Server. Un filtro ISAPI es básicamente una DLL cargada por el IIS que examina y modifica, si ello es necesario, los datos de entrada y/o salida del IIS. Los ámbitos de aplicación pueden afectar a seguridad-encriptación, compresión, logs, autenticación, etc. Las extensiones amplían y mejoran las funcionalidades del servidor (IIS), proporcionando una funcionalidad semejante a la de los CGI, permitiendo así crear contenidos dinámicos. El servidor IIS delega en la extensión la creación de los datos solicitados, pudiendo descargar la DLL transcurrido un tiempo sin actividad. La ventaja de usar extensiones es que se puede trabajar a nivel de cabeceras HTTP de forma fácil.

Figura 3. La aplicación desarrollada usará el método “POST” para enviar las peticiones del administrador.

La estructura y ciclo de ejecución de una extensión IIS Una extensión de IIS incluye normalmente la definición de tres funciones:  G e t E x t e n s i o n V e r s i o n es el punto de entrada de una extensión ISAPI y se ejecuta siempre que se recarga la extensión. Su signatura es: BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVer);

El parámetro “pVer” es un puntero a una estructura que podemos rellenar con información acerca de la extensión (número de versión y texto descriptivo). Por ejemplo: BOOL WINAPI GetExtensionVersion(OUT HSE_VERSION_INFO *pVer) { pVer->dwExtensionVersion = MAKE LONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR); lstrcpyn(pVer->lpszExtensionDesc, “MidletServer for IIS”, HSE_MAX_EXT_DLL_NAME_LEN); return TRUE; } 

T e r m i n a t e E x t e n s i o n es la función que se llama cuando la extensión es descargada por el servidor. Esta función, aunque opcional, es interesante porque nos permite finalizar y liberar recursos que son usados durante la vida de la extensión. Su signatura es: BOOL WINAPI TerminateExtension (DWORD dwFlags);

El único parámetro, nos indica cuándo la extensión va a ser descargada. Si el valor es “HSE_TERM_ADVISORY_UNLOAD”, podehttp://digital.revistasprofesionales.com


DISPOSITIVOS MÓVILES

Creación de un sistema de distribución de Midlets (I)



mos retornar “FALSE” para indicar al servidor que no descargue la extensión. Si el valor es “HSE_TERM_MUST_UNLOAD”, la descarga de la extensión no se puede cancelar. H t t p E x t e n s i o n P r o c es invocada por el servidor IIS cada vez que se recibe una petición. Su prototipo es:





DWORD WINAPI HttpExtensionProc (EXTENSION_CONTROL_BLOCK *pEcb);

En ésta función es el lugar donde el programador debe incluir el tratamiento y respuesta a la petición realizada. Para ello nos proporciona un puntero a una estructura de tipo “EXTENSION_CONTROL_BLOCK” que como veremos es el elemento sobre el que pivota todo el código de una extensión IIS.







Figura 4. La aplicación desarrollada cuenta con un interfaz con todos los elementos necesarios para gestionar la administración de Midlets.

EXTENSION_CONTROL_BLOCK: La estructura Si hay un elemento fundamental en la programación de las extensiones IIS, ese es sin duda alguna la estructura “EXTENSION_ CONTROL_BLOCK”. Todos los datos que podemos obtener de una conexión y todas las funciones que podemos ejecutar referente a una petición vienen a través de una instancia de esta estructura. Los campos que contienen son:  D W O R D c b S i z e: Indica el tamaño de la propia estructura.  D W O R D d w V e r s i o n: Número de versión de la estructura. EL “HIWORD” contiene el número mayor de la versión y el “LOWORD” contiene el número de versión menor.  H C O N N C o n n I D: Es el handle asociado a la conexión establecida entre el cliente y el servidor. Nos permite poder leer y escribir por los canales de lectura y escritura. No se debe modificar su valor. http://digital.revistasprofesionales.com











D W O R D d w H t t p S t a t u s C o d e: Es el código de estado de la transacción en curso cuando la petición haya finalizado. C H A R l p s z L o g D a t a [ H S E _ L O G _ B U FF E R _ L E N ]: Permite indicar una cadena de caracteres acabada con terminador nulo para que se escriba en el fichero de logs del servidor IIS y que puede ser usado con propósitos de análisis por parte del administrador. L P S T R l p s z M e t h o d: Es un puntero a una cadena con el método de conexión (“GET”, “POST”...) L P S T R l p s z Q u e r y S t r i n g : Es una cadena de caracteres que contiene la consulta o query de una petición (recordar el formato de una URL). Es equivalente a usar la función “Get ServerVariable” especificando como nombre de variable “QUERY_STRING”. L P S T R l p s z P a t h I n f o: Es una cadena de caracteres que contiene la vía de acceso proporcionada por el cliente. Es equivalente a usar la función “Get ServerVariable” especificando como nombre de variable “PATH_INFO”. L P S T R l p s z P a t h T r a n s l a t e d: Es equivalente a usar la función “GetServerVariable” especificando como nombre de variable “PATH_TRANSLATED”. L P S T R l p s z C o n t e n t T y p e: Contiene el valor de la cabecera HTTP “ContentType”. L P B Y T E l p b D a t a: Es un puntero al buffer que contiene los datos enviados por el cliente. Aunque no tiene porque contener todos los datos enviados. D W O R D c b T o t a l B y t e s: Es el número total de bytes recibidos desde el cliente en el “entity”. Es el valor de la cabecera HTTP “Content-Length”. D W O R D c b A v a i l a b l e : Número de bytes disponibles en el buffer apuntado “lpbData”. Si coincide con el valor de “cbTotalBytes”, todos los datos están en el buffer. En caso contrario se debe leer el resto usando la función “ReadClient”.



G e t S e r v e r V a r i a b l e permite obtener el valor de todas las variables de servidor relacionadas con la petición. Además nos permite acceder a los headers HTTP de la petición en curso. Su prototipo es: BOOL (WINAPI * GetServerVariable)(HCONN hConn, LPSTR lpszVariableName, LPVOID lpvBuffer, LPDWORD lpdwSize);

Donde “hConn” es el handle que indica el canal de comunicación en uso. “lpszVariableName” hace referencia a la variable de la que se quiere obtener el valor. En el cuadro “Variables del servidor” podemos ver algunas de las variables que podemos consultar. Para acceder a las headers HTTP, se debe especificar el nombre de la cabecera en mayúsculas sustituyendo los guiones por línea de subrayado y con el prefijo “HTTP_”. Por ejemplo, para conocer el valor de la cabecera “Content-Type” tendríamos que especificar como nombre de la variable “HTTP_CONTENT_TYPE”. En el cuadro “Campos de las cabeceras HTTP más usuales” podemos ver algunas de las variables. “lpvBuffer” es el buffer donde se devuelve el valor de la función. “lpdwSize” es un puntero a una variable que indica el tamaño máximo del buffer. Al finalizar la petición indica el número de bytes que ocupa la respuesta: std::string GetVariable(EXTENSION_CONTROL_BLOCK *pECB, const std::string & inName){ char szValue[512]; DWORD dwBuffSize = sizeof(szValue) - 1; /*”HTTP_USER_AGENT” “ALL_HTTP” “ALL_RAW”... o cualquier otra*/ if(pECB->GetServerVariable(pECB>ConnID, (LPSTR)inName.c_str(), szValue, &dwBuffSize)) return std::string(szValue); return std::string(“”); }

Las APIs del IIS Todas las APIs de IIS que se pueden ejecutar en una extensión con respecto a una conexión, las proporciona la instancia de la estructura “EXTENSION_CONTROL_BLOCK”. Esta estructura define cuatro punteros a funciones:



W r i t e C l i e n t nos proporciona la forma de escribir datos por el canal de respuesta. Su prototipo es: BOOL (WINAPI * WriteClient)(HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes, DWORD dwReserved);

23

SOLO PROGRAMADORES nº 127


DISPOSITIVOS MÓVILES

Variables del servidor Variable

Descripción

ALL_HTTP

Devuelve todas las cabeceras HTTP que se han recibido pero anteponiendo el prefijo “HTTP_”.

ALL_RAW

Devuelve todas las cabeceras HTTP que se han recibido sin procesar.

APPL_PHYSICAL_PATH

Path físico donde está situada la extensión.

CONTENT_LENGTH

Número de bytes en el “entity” o cuerpo de la petición en curso.

CONTENT_TYPE

El tipo de contenido del “entity”.

PATH_INFO

Es la porción de la URL después del nombre de la extensión (DLL) y antes de la query si se ha proporcionado.

PATH_TRANSLATED

Conversión del PATH_INFO a su directorio físico real.

REMOTE_ADDR

Proporciona la IP del cliente o agente (en el caso de haber un cortafuegos, proxy o gateway) que ha enviado la petición. Proporciona el nombre del cliente o agente que ha enviado la petición. El nombre del usuario que se ha conectado. Si el nombre de usuario no necesita autenticación (es anónimo) el valor será una cadena vacía.

REMOTE_HOST REMOTE_USER

Donde “lpvBuffer” apunta al buffer que contiene los datos a enviar al cliente. “lpdwSize” es un puntero a una variable que contiene el número de bytes a leer y que retorna el número de bytes leídos.  S e r v e r S u p p o r t F u n c t i o n nos permite ejecutar ciertas funciones de propósito general específicas a la versión en uso de IIS. Su signatura es: BOOL (WINAPI * ServerSupportFunction)(HCONN hConn, DWORD dwHSERequest, LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType);

El parámetro “dwHSERequest” indica la funcionalidad a usar. Los valores que puede tomar son: HSE_REQ_SEND_URL_REDIRECT_RESP HSE_REQ_SEND_URL HSE_REQ_SEND_RESPONSE_HEADER HSE_REQ_DONE_WITH_SESSION HSE_REQ_END_RESERVED HSE_REQ_MAP_URL_TO_PATH

REQUEST_METHOD

El método de conexión (“GET”, “POST”...).

HSE_REQ_GET_SSPI_INFO

SERVER_NAME

Es el nombre de Host o IP de la máquina del servidor.

HSE_REQ_TRANSMIT_FILE

SERVER_PORT

Es el puerto por el que se ha recibido la petición.

SERVER_PROTOCOL

Especifica el nombre y versión del protocolo asociado a la petición (HTTP/1.x).

SERVER_SOFTWARE

Nombre y versión del web server. dos. “dwReserved” sirve para indicar si la escritura va a ser síncrona o asíncrona. Los valores que puede tomar son: HSE_IO_SYNC

Nosotros, en nuestra aplicación, usaremos la función “ServerSupportFunction” con el parámetro “HSE_REQ_SEND_RESPONSE_HEADER”. En este caso el parámetro “lpvBuffer” se utiliza para indicar el código de estado a retornar. Si el puntero es nulo es equivalente a explicitar “200 OK”. “lpdwSize” indica el tamaño del buffer “lpdwDataType” que sirve para cabeceras opcionales.

HSE_IO_ASYNC



Figura 5. Todas las peticiones y acciones importantes de la aplicación quedan registradas en los ficheros diarios de log.

El parámetro “Buffer” apunta a los datos que se quieren enviar al cliente. “lpdwBytes” es un puntero a una variable que contiene el número de bytes a enviar y que retorna el número de bytes envia-

SOLO PROGRAMADORES nº 127

24

En nuestra aplicación usaremos el envío de datos síncronos, aunque en aplicaciones de grandes requisitos pueda ser aconsejable el uso de la escritura asíncrona. R e a d C l i e n t es usado para leer los datos del “entity” que no están disponibles directamente en el buffer apuntado por el campo “lpbData” de la estructura “EXTENSION_CONTROL_BLOCK”. Su signatura es: BOOL (WINAPI * ReadClient)(HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);

Conclusiones En esta entrega hemos visto los elementos básicos para implementar una extensión IIS. Si bien lo aprendido puede ser usado para la creación de una extensión con propósitos múltiples, en el próximo artículo nos centraremos en la aplicación de los conceptos para crear nuestro sistema de distribución OTA que es el objetivo final de la serie. El lector encontrará en el código fuente que complementa a este artículo una minuciosa explicación de cómo ejecutar el administrador de Midlets. http://digital.revistasprofesionales.com


MIDDLEWARE

Novedades en los lenguajes de .NET 2.0 (y III) GUILLERMO SOM ‘EL GUILLE’ (MVP de Visual Basic desde 1997)

Para finalizar esta serie de tres artículos sobre las novedades de .NET Framework 2.0 y los dos lenguajes principales (VB y C#), vamos a ver una de las características que más darán que hablar, y de la que también se escribirá bastante: los tipos “generic”. Generics

Los tipos “generic” permiten la creación de tipos de uso general sin pérdida de rendimiento LISTADO 1

Hemos dejado para esta tercera y última entrega una de las novedades más importantes que la nueva versión de .NET Framework 2.0 nos trae. Como comentamos de forma introductoria en el primer artículo de esta serie (véase Sólo Programadores 125), con “generics” podemos crear o usar clases con mayor rendimiento y control, con la ventaja añadida de que una misma clase o colección puede sernos igualmente útil sin importarnos (o casi) el tipo de datos que finalmente contendrá. Para no reinventar la rueda, vamos a utilizar un ejemplo “clásico” que clarifica perfectamente la utilidad de utilizar colecciones “generics” frente a las colecciones “clásicas”. Cuando creamos una colección en .NET 1.x, el tipo de datos que internamente usa la colección es “Object”. Esto es así, ya que al ser el tipo “básico” de todos los tipos de .NET nos permite almacenar cualquier tipo de datos. El problema que tienen las colecciones de tipo “Object”, es que si bien podemos añadir nuevos elementos sin mucha pérdida de ren-

Ejemplos con colecciones normales y del tipo generic (VB)

Sub Main() Dim lista1 As New System.Collections.ArrayList lista1.Add(“Pepe”) lista1.Add(12) Dim lista3 As New System.Collections.Generic.List(Of String) lista3.Add(“Hola”) lista3.Add(22) End Sub

SOLO PROGRAMADORES nº 127

26

dimiento (salvo en el caso de los tipos por valor), ese rendimiento se ve penalizado cuando accedemos al contenido de la colección, ya que debemos hacer una conversión (cast) para poder recuperar el valor en el tipo que originalmente usamos para almacenarlo. Aunque esta “peculiaridad” no es la única desventaja, ya que al ser una colección de tipo “Object”, no podremos controlar qué tipo de datos se añaden, ya que cualquier tipo que usemos será válido, por la sencilla razón de lo que hemos comentado antes: todos los tipos de .NET se derivan de “Object”. Para poder restringir el tipo de datos, debemos hacer nuestras propias comprobaciones en el código, ya que, por ejemplo, aunque nuestra intención sea almacenar solamente datos de tipo “Cliente”, nada nos impide añadir una cadena o un valor entero. La única forma que tendríamos de “admitir” sólo objetos de tipo “Cliente” sería creando nuestra propia colección personalizada.

Generics en VB y en C# En este artículo veremos los tipos “generic” tanto desde el punto de vista del desarrollador de Visual Basic como el de Visual C# e intentaremos mostrar las diferencias al usarlos desde estos dos lenguajes.

Pero si la colección es “generic”, podemos hacer restricciones y de esa forma será el propio compilador el que se encargue de hacer las comprobaciones pertinentes, de forma que sólo admita los tipos de datos que hemos indicado al crear la colección. En los listados 1 y 2 (VB y C# respectivamente) podemos ver un ejemplo de estos dos casos, el primero es usando una colección de tipo “ArrayList” a la que podemos añadir datos de distintos tipos, y en el segundo, usamos una colección genérica (“List”) a la que “limitaremos” el tipo de datos que podemos añadir para que sólo admita cadenas. La primera colección del tipo “ArrayList” admite cualquier tipo de datos, si nuestra intención es mantener sólo elementos numéricos, nada nos impide agregar uno de cualquier otro tipo, sin embargo, en la segunda colección hemos usado una del espacio de nombres “System.Collections.Generic” a la que le indicamos que el tipo de datos que contendrá será de tipo cadena (“string”). Esta colección no sólo contendrá http://digital.revistasprofesionales.com


MIDDLEWARE

Novedades en los lenguajes de .NET 2.0 (y III)

LISTADO 2

Ejemplos con colecciones normales y del tipo generic (C#)

static void Main(string[] args) { System.Collections.ArrayList lista1 = new System.Collections.ArrayList(); lista1.Add(“Pepe”); lista1.Add(12); System.Collections.Generic.List<string> lista3 = new System.Collections.Generic.List<string>(); lista3.Add(“Hola”); lista3.Add(22); }

“internamente” objetos del tipo indicado sino que en tiempo de compilación se comprobará que el valor asignado sea del tipo correcto, en caso contrario se producirá un error, que en el caso de Visual Basic, éste se mostrará justo cuando escribamos el código, mientras que si elegimos C#, el error se producirá al compilar. Además, el IDE de Visual Basic nos ofrece ayuda para corregir el error, tal como podemos ver en la figura 1.

El IDE de Visual Basic Realmente esta ayuda que ofrece Visual Basic no está relacionada con los “generics”, sino con el propio IDE. De esta y otras “peculiaridades” o novedades del entorno de desarrollo de Visual Studio nos ocuparemos en próximos números de la revista.

código de Visual Basic y C# es mutuamente compatible, ya que el código IL (Intermediate Language o lenguaje intermedio) generado por los compiladores es el que finalmente se usará en tiempo de ejecución. Esto significa que es el CLR el que realmente se encarga de gestionar todo lo relacionado con las clases y colecciones “generic”. Es decir, no es nada artificial que los compiladores de VB y C# tengan que hacer, sino que es algo intrínseco del propio .NET.

Los tipos de colecciones generic

Las colecciones “normales” de .NET (las que no son “generic”) se dividen principalmente en tres tipos, según la interfaz que implementen:  Las basadas exclusivamente en Como podemos comprobar en el listado 1, para “ICollection”, como “Stack” o “Queue”. declarar una colección “generic” en Visual  Las basadas en “IList”, como “ArrayList” y Basic debemos usar la instrucción “Of” seguida todas las que accedan al valor que contiedel tipo de datos que queremos que contenga nen mediante un índice numérico. dicha colección. Esta sintaxis es muy útil para  Las basadas en “IDictionary”, como los angloparlantes, ya que se leería como: “list “Hashtable” y todas las que accedan al of string” (lista de string). En C# la sintaxis es valor que contienen mediante una clave. diferente y se utiliza el tipo de datos a usar por Estas interfaces indicarán el comportamiento la colección “generic” encerrado entre signos de cada una de las colecciones, por ejemplo, de mayor y menor. Aunque la recomendación las que están basadas en “IList” se comportapara “leer” este tipo de declaraciones es de la rán de forma distinta a las que estén basadas misma manera que en VB: lista de string. en “IDictionary”, al menos en lo que se refiere Esta peculiaridad (o forma de actuar) de los a la hora de añadir, recuperar o recorrer los “generics” está implementada en el propio CLR elementos que contienen. En el caso de las (el runtime de .NET Framework), por tanto el colecciones de tipo diccionario los elementos que contienen siempre están formados por un par clave/valor, y normalmente para recuperar el valor guardado debemos indicar la clave que tiene relacionada. Por otro lado, las basadas en la interfaz “IList” los eleFigura 1. Error y sugerencia para solucionar el error de asignación mentos se almacede tipo no adecuado. http://digital.revistasprofesionales.com

nan de forma que podamos acceder a ellos indicando la posición que ocupan, como si de un array se tratase, de hecho, los arrays (que realmente están basadas en la clase “Array”), también implementan la interfaz “IList”. Para no crear confusión, debemos aclarar que tanto las colecciones basadas en “IList” como las que se basan en “IDictionary” también se basan o implementan la interfaz “ICollection”, de hecho todas las clases del espacio de nombres “System.Collections” se basan en esa interfaz. ¿Y que tiene que ver todo esto con los tipos de colecciones generic? Pues que todas las colecciones del espacio de nombres “System.Collections.Generic” también están basadas en estos tres tipos de interfaces, aunque en las versiones “generic” de las mismas, es decir, las interfaces también definen un parámetro con el tipo de datos que internamente manipularán. Al igual que ocurre con las colecciones no “generic”, todas las colecciones del espacio de nombres “System.Collections.Generic” se basan en la interfaz “ICollection”, aunque sólo unas pocas clases son las que utilizan esa interfaz de forma exclusiva, tal como ocurre con su hermana no “generic”. En el espacio de nombres “Generic” también encontraremos colecciones basadas en las otras dos interfaces: “IList” e “IDictionary”. De hecho la colección “Generic.List”, (usada en el listado 1), es una colección que implementa la interfaz “IList”. Como es de suponer, todas estas interfaces (y sus colecciones derivadas), al ser del tipo “generic”, nos sirven para usarlas con cualquier tipo de datos, (el indicado en el constructor). Después de crearlas, las usaremos como cualquier otra colección. Es importante destacar que en el caso de las colecciones basadas en “Generic.IDictionary” debemos tener en cuenta un pequeño detalle, y es que las colecciones de tipo “IDictionary”, sean o no “generic”, para recorrer los elementos que contienen siempre tendremos que hacerlo utilizando un tipo especial, ese tipo nos permitirá acceder al par clave/valor que es lo que realmente la colección contiene. En las colecciones “normales” el tipo de datos que usaremos en el bucle es “DictionaryEntry”, mientras que en las colecciones “generic” usaremos un objeto del tipo “KeyValuePair”; tanto uno como otro contienen dos propiedades, “Key” y “Value”, con las que podremos acceder respectivamente a la clave y al valor propiamente dicho. En el caso del tipo “DictionaryEntry”, esas dos propiedades son de tipo “Object”, sin 27

SOLO PROGRAMADORES nº 127


MIDDLEWARE

embargo el tipo “KeyValuePair”, al ser “generic”, los tipos de las dos propiedades deben ir en consonancia con los indicados al instanciar la colección en la que lo usemos para recorrer los elementos. Aclaremos este punto. Al crear una colección basada en “Generic.IDictionary”, al contener elementos del tipo clave/valor, debemos indicar de qué tipo será tanto la clave como el valor, por tanto al recorrer los elementos usando un tipo “KeyValuePair”, también tendremos que indicar los tipos a los que accederá. En los listados 3 y 4 (para VB y C# respectivamente) tenemos un ejemplo en el que almacenamos valores de tipo “Cliente” y para la clave utilizamos un tipo “String”. Como puede verse en los listados, al declarar la colección indicamos los tipos a usar, añadimos algunos valores y para recorrer todos los elementos usamos un bucle “For Each” con una variable del tipo “KeyValuePair” a la que le indicamos los mismos tipos que contiene la colección (véase también la figura 2). Tal como podemos comprobar, al acceder a cada elemento, ya sea directamente mediante el indizador (propiedad predeterminada) o por medio de la variable usada en el bucle, podemos utilizar directamente las propiedades de la clase almacenada (del tipo “Cliente”). Si esto mismo lo hubiésemos hecho con una colección no “generic”, tendríamos que haber hecho una conversión del tipo interno al tipo “real”. En los listados 5 y 6 podemos ver el código equivalente usando una colección del tipo “Hashtable” (no “generic”). Como vemos, en las colecciones “normales” siempre se usa un objeto de tipo “Object” para almacenar los valores, incluso par clave/valor del tipo “DictionaryEntry” también es de tipo “Object”, lo que nos obliga a hacer una conversión del tipo interno al tipo “real”, en nuestro ejemplo de tipo “Cliente”. Incluso si definiéramos una colección personalizada nos veríamos obligados a hacer dicha conversión de datos aunque fuese de forma interna, ya que externamente al estar “preparada” para soportar objetos de tipo “Cliente” no tendríamos que hacer ninguna conversión. Con idea de no alargar el artículo con demasiado código extra, los ejemplos para este caso y las definiciones de la clase “Cliente” se incluyen en el código que acompaña a la revista.

Métodos generic Tal como hemos comprobado, existe un espacio de nombres en el que se incluyen las colec-

SOLO PROGRAMADORES nº 127

28

LISTADO 3

Ejemplo de colección Generic.Dictionary (VB)

Dim di As New Dictionary(Of String, Cliente) di.Add(“uno”, New Cliente) di.Add(“dos”, New Cliente(“Pepe”, “López”)) di(“uno”).Nombre = “Juan” di(“uno”).Apellidos = “Ruiz” For Each kv As KeyValuePair(Of String, Cliente) In di Console.WriteLine(“Clave: {0} Valor: {1}”, _ kv.Key, kv.Value.Nombre) Next

LISTADO 4

Ejemplo de colección Generic.Dictionary (C#)

Dictionary<string, Cliente> di = new Dictionary<string, Cliente>(); di.Add(“uno”, new Cliente()); di.Add(“dos”, new Cliente(“Pepe”, “López”)); di[“uno”].Nombre = “Juan”; di[“uno”].Apellidos = “Ruiz”; Console.WriteLine(“Usando Dictionary”); foreach(KeyValuePair<string, Cliente> kv in di) Console.WriteLine(“Clave: {0} Valor: {1}”, kv.Key, kv.Value.Nombre);

LISTADO 5

Ejemplo de colección Hashtable (VB)

Dim di As New Hashtable di.Add(“uno”, New Cliente) di.Add(“dos”, New Cliente(“Pepe”, “López”)) TryCast(di(“uno”), Cliente).Nombre = “Juan” CType(di(“uno”), Cliente).Apellidos = “Ruiz” For Each de As DictionaryEntry In di Console.WriteLine(“Clave: {0} Valor: {1}”, _ de.Key, CType(de.Value, Cliente).Nombre) Next

Figura 2. Las colecciones “generic” saben contienen.

ciones “generic”, por tanto todas las clases o tipos incluidos en ese espacio de nombres nos permitirán crear colecciones que utilicen esta nueva característica de .NET 2.0; pero la ventaja de esta nueva “tecnología” es que nosotros también podemos definir nuestros propios tipos de datos “generic”, con lo cual podremos crear clases y colecciones que puedan ser restrictivos a la hora de usarlos, y como veremos en breve, no sólo se podrán hacer restricciones por medio del tipo usado para declarar el objeto, sino que también podremos poner nuestras propias normas o condiciones a esos tipos, de forma que deban tener ciertas “características” para que poda-

mos usarlos para crear esos objetos. Pero antes de entrar en las interioridades de los tipos “generic”, veamos cómo podemos crear métodos con parámetros de tipo genérico, de forma que podamos usarlos con cualquier tipo de datos. qué tipos de datos Para definir un método con argumentos genéricos, simplemente usamos la misma sintaxis que para las colecciones del espacio de nombres “Generic”. El código del listado 7 nos permitirá llamar al método “Mostrar” usando cualquier tipo. Este método mostrado en el listado 7 podemos usarlo indicando cualquier tipo de datos, por ejemplo: Mostrar(10) Mostrar(“Hola”)

Cuando usamos este método, el tipo indicado en Visual Basic con “Of”, (“T” en el ejemplo), o el incluido dentro de “<>” si estamos usando C#, lo podemos usar como el tipo de http://digital.revistasprofesionales.com


MIDDLEWARE

Novedades en los lenguajes de .NET 2.0 (y III)

LISTADO 6

Ejemplo de colección Hashtable (C#)

Hashtable di = new Hashtable(); di.Add(“uno”, new Cliente()); di.Add(“dos”, new Cliente(“Pepe”, “López”)); ((Cliente)di[“uno”]).Nombre = “Juan”; ((Cliente)di[“uno”]).Apellidos = “Ruiz”; foreach(DictionaryEntry de in di) Console.WriteLine(“Clave: {0} Valor: {1}”, de.Key, ((Cliente)de.Value).Nombre);

LISTADO 7

Un método con argumentos generic

‘ Código para Visual Basic Sub Mostrar(Of T)(ByVal p As T) Console.WriteLine(p) End Sub // Código para C# static void Mostrar<T>(T p) { Console.WriteLine(p); }

datos que se usará al llamar al método. Por esa razón se puede usar esa “letra” para indicar un tipo de datos, que es el caso del argumento pasado al método. Es decir, el tipo “genérico” sólo se conocerá cuando se use el método, pero nosotros podemos hacer referencia a él usando el “parámetro con tipo” (type parameter). El problema que nos podemos encontrar en estos casos es que no podemos hacer grandes cosas con ese tipo dentro del método. Por la sencilla razón de que ese tipo puede ser de cualquier tipo (valga la redundancia), por tanto no podremos llamar a métodos que “suponemos” que pueda tener, ni realizar operaciones aritméticas, etc. Realmente los únicos métodos que podremos usar son los propios del tipo “Object”, por la sencilla razón de que están presentes en todos los tipos de datos de .NET. Sabiendo esto, podríamos pensar que mejor sería indicar el parámetro como de tipo “Object” y no nos complicamos tanto la vida. Pero si así fuera, que como veremos no estamos tan restringidos, también valdría la pena usar “generic”, la principal razón es la que apuntamos anteriormente de que el tipo usado realmente es el tipo que indiquemos al llamar al método (o cualquier otro elemento que utilice “generic”), por tanto el rendimiento lo tenemos asegurado, ya que en el caso de los tipos por valor, el CLR no se verá en la necesidad de hacer boxing y unboxing, es decir, convertir de tipo por valor a tipo por referencia y viceversa. Y en el caso de los tipos por referencia no tendremos que hacer conversiones para convertirlo de “Object” al tipo “real”. http://digital.revistasprofesionales.com

Declaración de tipos generic en VB y C# La forma de declarar los tipos “generic” es algo con lo que tendremos que acostumbrarnos a convivir, ya que este tipo de datos lo utilizaremos con bastante frecuencia, no sólo porque ahora vayamos a definir muchos de nuestros tipos o métodos usando “generic”, sino porque la nueva versión de .NET está plagada de ellos, por tanto, los utilizaremos con bastante frecuencia. Si bien es cierto que la sintaxis de C#, aunque en principio pudiera parecer más rebuscada, a la larga resulta más intuitiva o al menos no tan “repetitiva” como la de Visual Basic, ya que en este último lenguaje tanto para la declaración del tipo que contendrá como para los parámetros, (tal como podemos comprobar en el código del listado 7), debemos indicarlos entre paréntesis, con lo que puede parecer una redundancia que hasta que no nos acostumbremos a usarlos, seguramente nos “complicará” un poco la existencia.

Restricciones (constraints) En el código del listado 7 simplemente indicamos que se usan tipos “generic”, y tal como hemos comentado no podemos hacer ningún tipo de operación con el objeto usado internamente, salvo usar los métodos y propiedades de la clase “Object”. Por tanto, si queremos hacer “algo” dentro del método “Mostrar”, no podríamos, ya que no sabemos cuál será el tipo de datos que el usuario finalmente usará. ¿Cómo solucionamos estos inconvenientes? Usando restricciones. Aunque parezca lo contrario, las restricciones nos permiten “afinar” en el tipo que podremos pasar como argumento. Supongamos que queremos usar sólo tipos por valor en nuestro método (o clase “generic”), simplemente hacemos una restricción para que el compilador sepa que esa es nuestra intención. O bien podemos restringir a tipos de datos que se deriven de otros o que implementen cierta interfaz o que tenga un constructor o que sea un tipo por referencia. En el código de los listados 8 y 9 hemos creado una versión del método “Mostrar” para que sólo acepte como argumento un objeto que

sea de tipo por valor, por tanto sólo admitirá los tipos por valor definidos en los lenguajes (y el propio .NET), así como todas las estructuras que nosotros definamos. Con esta restricción lo único que conseguimos es “no permitir” usar valores de tipos por referencia, pero tampoco podemos hacer ningún tipo de operación con el parámetro que recibe el método. Aunque también podemos hacer restricciones que “afinen” más, es decir, podemos hacer múltiples restricciones, por ejemplo, si queremos comparar los objetos pasados al método, podemos añadir una nueva restricción: que implemente la interfaz “IComparable”. Antes de ver el código, (el cual recordemos que podemos obtener al completo en el material complementario de este artículo), debemos fijarnos en cómo se hacen las restricciones tanto en VB como en C#. En el caso de Visual Basic, la restricción la hacemos indicando “As <tipo>” junto a la declaración del tipo a usar en el método, mientras que en C# las restricciones las haremos usando “where” y después del “tipo de parámetro” (separado por dos puntos) indicaremos la restricción.

Hacer varias restricciones Cuando queramos hacer más de una restricción, la sintaxis de C# sigue siendo la misma, es decir, después de “where <tipo del parámetro> :” indicaremos cada una de las restricciones que creamos oportunas, simplemente separándolas con una coma. Mientras que en Visual Basic la sintaxis cambia un poco, ya que si hay más de una restricción estas deben incluirse dentro de un par de llaves y dentro de las llaves las separaremos con comas: “<tipo del parámetro> As {restricción1, restricción2, ..., restricciónN}”. En el código del listado 10 podemos ver las declaraciones de un método “Mostrar” que restrinja los datos a usar para que sólo se acepten tipos por valor y tipos que implementen la interfaz “En este código utilizamos dos parámetros que son del mismo tipo que el parámetro con tipo (type parameter) que podemos usar: los dos tienen las mismas restricciones. Pero también podemos indicar más de un parámetro “tipado” de forma que los parámetros del método puedan ser de cualquiera de esos tipos, e incluso de tipos “normales”, es decir, tipos que no tengan porqué estar definidos en la lista de “parámetros con tipo”. Para indicar más de un tipo de parámetro del método genérico (esto también será aplicable 29

SOLO PROGRAMADORES nº 127


MIDDLEWARE

LISTADO 8

Un método que restringe el parámetro a tipos por valor (VB)

‘ restringir a sólo tipos por valor (estructuras) Sub Mostrar1(Of T As Structure)(ByVal p As T) Console.WriteLine(p) End Sub

LISTADO 9

Un método que restringe el parámetro a tipos por valor (C#)

// restringir a sólo tipos por valor (estructuras) static void Mostrar1<T>(T p) where T : struct { Console.WriteLine(p); }

LISTADO 10

Declaración de un método con múltiples restricciones (VB y C#)

‘ Código para Visual Basic Sub Mostrar2(Of T As {Structure, IComparable})(ByVal v1 As T, ByVal v2 As T) // Código para C# static void Mostrar2<T>(T v1, T v2) where T : struct, IComparable

a las clases o tipos genéricos que definamos), lo haremos en el sitio donde se indican los parámetros con tipo, separándolos con comas. Esos tipos también podrán tener restricciones diferentes, (en un momento veremos cómo aplicarlas), para que de esta forma podamos hacer distinciones entre esos parámetros, y esas restricciones se tengan en cuenta al pasar los argumentos reales al método. En el listado 11 podemos ver cómo indicar dos tipos de parámetros con restricciones distintas. En la lista de parámetros del método podremos usarlos en conjunto con cualquier otro tipo de datos. Es decir, los parámetros del método se definen de la forma habitual, independientemente de que se utilicen tipos “normales” o los definidos en la lista de tipos de parámetros genéricos que se indicarán al llamar al método. En esta definición lo que hacemos es utilizar dos parámetros genéricos, el primero debe implementar la interfaz “IComparable”, el segundo debe ser un tipo por referencia además de implementar esa misma interfaz. En la lista de parámetros del método usamos esos dos tipos además de uno del tipo “DateTime”. Como podemos comprobar, la forma de definir los tipos de parámetros genéricos y las restricciones aplicables a cada uno dependerá de que lenguaje estemos usando. En VB siempre se hacen las restricciones en el mismo sitio en el que definimos los tipos y solamente usamos una vez la instrucción “Of”. Sin embargo en C#, la lista de tipos de parámetros la hacemos justo después de la definición del método y las restricciones las hacemos después de la lista de parámetros del método, para cada tipo utilizamos una instrucción “where” y las restricciones que correspondan. En ambos casos utilizamos la instrucción “class” para indicar que el argumento que pasemos al llamar al método debe ser un tipo por referencia, en caso de hacerlo junto

SOLO PROGRAMADORES nº 127

30

a otras restricciones, en C# esa instrucción debe aparecer antes que el resto de restricciones, en VB da igual donde lo indiquemos.

Tipos de restricciones que podemos hacer Tal como hemos estado viendo en los códigos de ejemplo, podemos hacer varios tipos de restricciones al indicar los parámetros con tipo, las restricciones que podemos usar son las siguientes: 

Que sea un tipo por valor, para ello utilizaremos “struct” (C#) o “Structure” (VB).

LISTADO 11



Que sea un tipo por referencia, lo indicaremos con “class” (C#) o “Class” (VB).  Que tenga un constructor sin parámetros, lo indicaremos con “new()” (C#) o “New” (VB).  Cualquier clase, en este caso el argumento indicado debe derivarse de esa clase.  Una o más interfaces, el argumento debe implementar todas las interfaces indicadas. Si indicamos que el argumento a pasar debe ser por valor o por referencia, en C# éste debe aparecer antes que el resto de restricciones, en VB puede aparecer en cualquier orden. Y si indicamos que debe tener un constructor sin parámetros, en C# lo debemos indicar al final de las restricciones, en VB no importa el orden. Cualquiera de los tipos o interfaces que indiquemos pueden ser normales o “ generic”.

Definir nuestros tipos generic Para ofrecer un repaso completo a los tipos genéricos, vamos a explicar cómo definir nuestros propios tipos genéricos, ya sean clases, estructuras o interfaces. La forma de definir nuestros propios tipos genéricos no varía mucho a como definimos los métodos genéricos. La parte importante es que podemos indicar uno o más tipos de parámetro a usar al declarar la clase, con sus correspon-

Varios tipos con distintas restricciones (VB y C#)

Sub Mostrar3(Of T1 As IComparable, T2 As {IComparable, Class})(ByVal v1 As T1, ByVal v2 As T2, ByVal v3 As DateTime) static void Mostrar3<T1, T2>(T1 v1, T2 v2, DateTime v3) where T1 : IComparable where T2 : class, IComparable {

LISTADO 12

Definición de una clase generic (VB)

Public Class ListaPersonas(Of T As Persona) Implements IEnumerable ‘ Private lista As New List(Of T) ‘ Public Sub Add(ByVal valor As T) lista.Add(valor) End Sub ‘ Default Public ReadOnly Property Item(ByVal index As Integer) As T Get Return lista(index) End Get End Property Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return lista.GetEnumerator End Function End Class

http://digital.revistasprofesionales.com


MIDDLEWARE

Novedades en los lenguajes de .NET 2.0 (y III)

LISTADO 13

Definición de una clase generic (C#)

public class ListaPersonas<T> : IEnumerable where T : Persona { // private List<T> lista = new List<T>(); // public void Add(T valor) { lista.Add(valor); } // public T this[int index] { get { return lista[index]; } } // public System.Collections.IEnumerator GetEnumerator() { return lista.GetEnumerator(); } }

LISTADO 14

Usando nuestra clase genérica (VB)

Dim emp As New ListaPersonas(Of Empleado) Dim cli As New ListaPersonas(Of Cliente) Dim per As New ListaPersonas(Of Persona) ‘ emp.Add(New Empleado(“Pepe”, “Sánchez”, 1200)) emp.Add(New Empleado(“Juan”, “Ruiz”, 1300)) emp.Add(New Empleado(“Luisa”, “Gómez”, 1500)) ‘ cli.Add(New Cliente(“Muebles Sánchez”, 12200)) cli.Add(New Cliente(“Electricas Unidas”, 7500.55D)) ‘ For Each p As Persona In emp per.Add(p) Next For Each c As Persona In cli per.Add(c) Next ‘ For Each p As Persona In per Console.WriteLine(“Tipo: {0}, Contenido: {1}”, p.GetType.Name, p) Next

LISTADO 15

Usando nuestra clase genérica (C#)

ListaPersonas<Empleado> emp = new ListaPersonas<Empleado>(); ListaPersonas<Cliente> cli = new ListaPersonas<Cliente>(); ListaPersonas<Persona> per = new ListaPersonas<Persona>(); // emp.Add(new Empleado(“Pepe”, “Sánchez”, 1200)); emp.Add(new Empleado(“Juan”, “Ruiz”, 1300)); emp.Add(new Empleado(“Luisa”, “Gómez”, 1500)); // cli.Add(new Cliente(“Muebles Sánchez”, 12200)); cli.Add(new Cliente(“Electricas Unidas”, 7500.55M)); // foreach (Persona p in emp) per.Add(p); foreach (Persona c in cli) per.Add(c); // foreach (Persona p in per) Console.WriteLine(“Tipo: {0}, Contenido: {1}”, p.GetType().Name, p);

dientes restricciones y ese tipo (o tipos) los podemos usar dentro de la clase para indicar el tipo de datos a usar en las propiedades, métodos, etc. que definamos. Aclaremos las cosas viendo un poco de código. En los listados 12 y 13 tenemos la definición de una clase/colección en la que utilizamos como parámetro de tipo http://digital.revistasprofesionales.com

genérico un objeto que sea o se derive de la clase “Persona”. Ese tipo está indicado con el símbolo “T”. Ese tipo “anónimo” lo utilizamos para la colección interna como parámetro del método “Add” y como el valor devuelto por la propiedad predeterminada (indizador). Para almacenar los elementos que contenga la clase

de los listados anteriores utilizamos una colección “Generic.List” porque nos permite almacenar tipos “genéricos”, en este caso del tipo “T”, es decir el indicado al crear nuestra clase/colección. Los tipos de datos que se pueden añadir a nuestra colección serán del tipo “T” y el valor que devuelve la propiedad predeterminada (“Item” en VB, el indizador en C#), también será del mismo tipo que el usado al instanciar nuestra clase. Además de la definición de esta clase genérica, hemos creado tres clases: La clase “Persona” y otras dos clases derivadas de ésta. Como la restricción que hemos hecho es que sea del tipo “Persona”, también podremos usar como tipo de nuestra clase cualquiera que se derive de ella (en nuestro caso hemos definido una clase “Cliente” y otra “Empleado” que se derivan de “Persona”), por tanto podemos usar un código como el mostrado en los listados 14 y 15. En la clase “ListaPersonas” no hacemos ningún uso específico del tipo indicado como parámetro de tipo, por tanto podríamos pensar que no hacía falta indicar esa restricción, pero es que en este caso, nos interesaba que nuestra clase genérica solamente aceptara objetos que se deriven de la clase “Persona”, por eso hemos hecho esa “restricción”. De esta forma, si la clase “Persona” define algún método o propiedad que nos interese utilizar en el tipo indicado al crear nuestra clase genérica, lo podremos hacer sin ningún tipo de problema.

Conclusiones Confiamos en que lo aquí expuesto sirva para aclarar cómo funcionan los tipos genéricos y nos aliente a experimentar con ellos, ya que como suele ocurrir con las novedades de cualquier lenguaje, sólo se pueden saborear cuando realmente nos acostumbramos a utilizarlas, y eso es lo que hemos pretendido con esta serie de tres artículos sobre las novedades de .NET Framework 2.0 y de los dos lenguajes principales de la familia de Visual Studio 2005: mostrar las novedades más importantes y tratar de explicar, con las limitaciones de espacio propias de una revista, cómo podemos usarlas. Precisamente por eso incluimos el código de ejemplo completo en el CD de la revista, código que debemos advertir que es válido para la versión beta 2 de Visual Studio 2005 y que se puede usar indistintamente con las versiones Express de los lenguajes, y como no es costumbre que después de una beta 2 haya muchos cambios en los lenguajes, esperamos que sigan siendo válidos en la versión definitiva. 31

SOLO PROGRAMADORES nº 127


MIDDLEWARE

Struts práctico (II) ÓSCAR ARANDA CRESPO (Arquitecto J2EE)

En la primera entrega de este curso sobre Struts vimos una introducción a este popular framework de desarrollo J2EE. En este artículo veremos aspectos más avanzados que nos simplificarán enormemente el desarrollo de las interfaces de usuario, sin duda una de las facetas que más tiempo consume en el desarrollo de aplicaciones. Introducción En el número anterior vimos lo que nos puede aportar Struts a la hora de desarrollar aplicaciones J2EE e incluimos una aplicación de referencia donde plasmamos los conceptos básicos de la arquitectura MVC que propone Struts (Actions, ActionMappings, ActionsForms, librerías de etiquetas). En este artículo vamos a centrarnos en aspectos más avanzados que, aunque no son imprescindibles en el desarrollo de aplicaciones básicas, sí nos facilitan mucho la vida a la hora

de abordar aplicaciones de cierta complejidad. En primer lugar habilitaremos nuestra aplicación para el soporte de varios idiomas (haremos que nuestra aplicación de ejemplo soporte los idiomas inglés y español). Luego veremos cómo usar plantillas a la hora de diseñar la interfaz de usuario, un concepto que no existe en J2EE y que introduce el componente Tiles de Struts. Por último veremos cómo realizar las validaciones de campo en las interfaces de usuario de una forma sencilla, basada en la definición de ciertas reglas mediante ficheros XML, usando el framework de Jakarta commons-validator.

Internacionalización Actualmente, es muy común que las aplicaciones web deban ofrecer sus contenidos adaptados a usuarios de distintos países y con distintos idiomas. Lógicamente, esto no puede traducirse en que tengamos que duplicar los elementos de la interfaz de usuario (en nuestro caso, páginas JSP) para cada nuevo idioma que queramos soportar. Esto dificultaría enormemente el mantenimiento de las aplicaciones y la solución debe pasar por tener una única página JSP que dinámicamente muestre su contenido en el idioma del usuario que la está visitando.

Resource Bundles en Java

Registro de un usuario utilizando la interfaz en Español.

SOLO PROGRAMADORES nº 127

32

La plataforma Java ya tiene resuelta esta situación mediante lo que se conoce como “Resource Bundes”. Se trata de externalizar todos aquellos recursos que utilice la aplicación en distintos ficheros “properties”. Cada usuario tiene asociado un “Locale” que consiste en un identificador de un idioma en un determinado país. Así por ejemplo, para identificar el idioma español se usa el identificador “es” y para referimos a la localización de España, usaremos “ES” (los identificadores de cada idioma y cada país vienen definidos por los estándares ISO-639 e ISO 3166 respectivamente). El Locale completo sería “es_ES”. Java utiliza este identificador (Locale), para decidir en qué fichero se deben obtener los recursos solicitados (normalmente textos o imágenes). El mecanismo de búsqueda es muy sencillo. Imaginemos que un usuario tiene configurado su navegador con el Locale “en_US” (inglés de Estados Unidos). Si por ejemplo se requiere http://digital.revistasprofesionales.com


MIDDLEWARE

Struts práctico (II)

un mensaje del Resource Bundle “mensajes”, en primer lugar se buscaría el fichero “mensajes_en_US.properties”. Si este fichero no existiese, se buscaría “mensajes_en.properties”, es decir, el fichero con mensajes en inglés, sin especificar lugar. Por último, si dicho fichero tampoco existiese se mostrarían los mensajes del fichero por defecto, “mensajes.properties”. Los ficheros “properties” consisten en ficheros de texto plano que contienen los mensajes a mostrar por la aplicación. Cada línea cosiste en un identificador único de mensaje seguido del signo “=” y el contenido del mensaje en el idioma del fichero en concreto. Esta última parte es la que variará entre los ficheros “properties” para los distintos idiomas. En la aplicación que acompaña al artículo podemos ver en el directorio “WebContent/WEBINF/source/com/sp/spshop/resources” que contamos con dos “Resource Bundles”: “mensajes” y “pantallas”. Cada uno de ellos contiene dos ficheros, uno para inglés (“mensajes_en.properties” y “pantallas_ en.properties”) y otro como idioma por defecto en español (“mensajes.properties” y “pantallas.properties”). Se puede observar como todos los ficheros de cada bundle contienen los mismos identificadores de mensaje, aunque con distinta traducción. Struts se basa en este mecanismo del que dispone Java para la internacionalización (abreviado con “i18n”) y que acabamos de explicar. Según el patrón de diseño MVC

Model 2, que sigue Struts, los mensajes mostrados en la interfaz de usuario pueden provenir de dos fuentes:  Textos incluidos en el diseño de la interfaz de usuario.  Mensajes que se generen tras la ejecución de una operación (ya sean de error o simplemente informativos). Estos mensajes pueden lanzarse por ejemplo desde un ActionForm que tiene que informar que el valor de un determinado campo no esta permitido o por ejemplo, desde un Action que atrapó una excepción producida al invocar a la lógica de negocio. Struts nos ofrece facilidades para que estas dos tareas resulten más sencillas.

i18n en JSPs Lo común es que la mayor parte de los textos que se muestran al usuario y deben internacionalizarse estén en las páginas JSP. Ya vimos en el artículo anterior que Struts proporciona una potente librería de etiquetas para facilitar el desarrollo de JSPs. La mayor parte de las etiquetas usadas para representar controles gráficos HTML, soportan tres atributos extras para que sus contenidos sean mostrados en función del Locale particular de cada usuario. Estos tres atributos son:  b u n d l e : Identificador del Resource Bundle. Dado que la forma de referirse a un Resource Bundle en Java es mediante su localizacion completa (por ejemplo “com.sp.spshop.resources.pantallas”) y puede resultar un poco tedioso escribir estos identificadores en cada una de las etiquetas, Struts nos permite asignarles “alias” en el fichero “struts-config.xml”. Para ello usar la etiqueta XML “<messageresources>”. Ejemplo: Para los recursos relacionados con la interfaz de usuario definiremos el Resource Bundle: <message-resources parameter=”com.sp.spshop.resources. pantallas” key=”ui”> 

Registro de un usuario utilizando la interfaz en Inglés. http://digital.revistasprofesionales.com

l o c a l e: Su uso no es muy común e indica el nombre donde se almacena el objeto Locale en la session de cada usuario. Si no se usa, se toma el valor por defecto de Struts “org.apache.struts.Globals.LOCALE_KEY”.



k e y: Indica el identificador del mensaje al que nos referimos, por ejemplo: <html:option key=”spshop.lista.mediospago”/>

En algunas etiquetas este atributo variará su nombre. Por ejemplo, para mostrar una imagen mediante la etiqueta “<html:img>” o “<html:image>” usaremos “srcKey” o “pageKey” para que la URL de la imagen se extraiga del fichero “properties” correspondiente. Para indicar el texto alternativo (“alt”) o descripción (“title”) de los elementos HTML, usaremos “altKey” y “titleKey”. Por ejemplo la página “catalogo.jsp” contiene una imagen con el texto “Categorías” que lógicamente debe ser distinta en español que en inglés. Para ello pondremos: <html:img pageKey= “spshop.img.head.categorias” altKey=”spshop.img.head.alt. categorias” bundle=”ui” … />

Los textos normales dentro de una página HTML no necesitan estar dentro de una etiqueta. Para añadir el soporte i18n, tendremos que sustituirlos por una etiqueta que indique el identificador del mensaje y opcionalmente el bundle donde localizar el mensaje. La etiqueta “<bean:message>” se encarga de esto. Por ejemplo, para poner el titulo de la página de nuestra tienda pondremos (“head.jsp”): <head> <title> <bean:message key=”spshop.titulo” bundle=”ui”/> </title>

i18n en Actions y ActionForms Si observamos cualquiera de los Actions de la aplicación Sp.Shop, vemos que se sigue el esquema del listado 1. El Action crea una instancia de ActionMessages (objeto que almacena uno o más errores). En caso de que se produzca una excepción, se añadirá un mensaje mediante la creación de un objeto ActionMessage. Para crear una instancia de ActionMessage, es imprescindible indicar un identificador de mensaje (no se nos permite indicar un texto “hardcoded”), por lo que Struts nos obliga a seguir la buena 33

SOLO PROGRAMADORES nº 127


MIDDLEWARE

LISTADO 1

Esquema de los Actions de Sp.Shop

ActionMessages errors = new ActionMessages(); ActionForward forward = new ActionForward(); try { // invocar lógica de negocio } catch (Exception e) { errors.add(“error”, new ActionMessage(“error.id.XXX”)); } if (!errors.isEmpty()) { saveErrors(request, errors); forward = mapping.findForward(“error”); } else { forward = mapping.findForward(“ok”); }

LISTADO 2

Método validate de DatosLoginAction

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if ((usuario == null) || (usuario.length() == 0)) { logger.debug(“Falta usuario en petición de login”); errors.add(“usuario”, new ActionMessage(“error.login.faltaUsuario”)); } if ((contrasena == null) || (contrasena.length() == 0)) { logger.debug(“Falta contraseña en peticion de login”); errors.add(“usuario”, new ActionMessage(“error.login.faltaContrasena”)); } return errors; }

práctica de tener los mensajes de usuario externalizados. Si se produjo algún error, lo siguiente es guardar el objeto ActionMessages en la “request”, mediante el método “saveErrors()” y redirigir a una JSP que contenga la etiqueta “<html:errors>”. Esta etiqueta muestra por defecto los mensajes que hayan sido guardados en la “request” mediante el método “saveErrors()” aunque podríamos mostrar otro objeto ActionErrors que se hayan almacenado bajo cualquier otro nombre. Pero antes de que se pueda llegar a producir un error en la ejecución de un Action, es posible que la validación del ActionForm no sea satisfactoria y por tanto se deban incluir

los mensajes de error oportunos para mostrárselos al usuario. La firma del método “validate()” nos indica que se debe devolver un objeto ActionErrors. ActionErrors es una clase que hereda de ActionMessages y la idea es la misma que ya explicamos anteriormente. Cuando los ActionForms detecten algún error deberán añadir un nuevo ActionError al objeto ActionErrors que se devolverá. En caso de devolver “null” o un objeto ActionErrors vacío, la validación se entenderá que ha sido correcta. El listado 2 muestra el método validate del ActionForm DatosLoginForm. Se puede apreciar cómo cuando no se rellena el campo usuario o el campo contraseña, se añade un nuevo ActionError con el

LISTADO 3

Action CambiarLocale

public class CambiarLocaleAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ActionForward forward = new ActionForward(); try { String lenguaje = request.getParameter(“lenguaje”); Locale loc = new Locale(lenguaje); setLocale(request, loc); } catch (Exception e) { } forward = mapping.findForward(“ok”); return (forward); } }

SOLO PROGRAMADORES nº 127

34

identificador del mensaje que queremos mostrar al usuario. La página que posteriormente muestre los errores pintará el mensaje en el idioma correspondiente en función del Locale del usuario.

Gestión del Locale Una pregunta que habrá surgido al lector es, ¿cómo elige el usuario el Locale con el que quiere navegar? ¿Cómo podemos hacer que el usuario pueda modificar este Locale? Struts almacena en la sesión de cada usuario el objeto Locale (“java.util.Locale”) bajo la clave “org.apache.struts.Globals.LOCALE_KEY”. Cada vez que hay que buscar un mensaje con soporte i18n se consulta este valor y se busca en el “properties” correspondiente. Ahora bien, ¿Quien se encarga de asignar el valor inicial de esta variable de sesión? Si se pregunta por el Locale y este no tiene valor inicial (normalmente, porque el usuario acaba de entrar en la aplicación), entonces Struts asigna uno por defecto basándose en los campos de la cabecera HTTP “Accept-language”, es decir, en el lenguaje que haya configurado el usuario en su navegador. Para permitir al usuario el cambio de Locale, basta con modificar el valor de la variable de sesión “Globals.LOCALE_KEY” mediante el método “setLocale()” disponible en cualquier Action. Veamos ahora en la aplicación Sp.Shop el Action encargado de cambiar el Locale. Este Action recibirá como parámetro el lenguaje que se quiere asignar al usuario (véase el listado 3).

Struts Tiles Un problema común a la hora de mantener un sitio web suele consistir en la dificultad de aplicar cambios de diseño a todas las páginas que la componen, pues muchas veces implica tener que modificar cada una de estas páginas. La especificación JSP incluye mecanismos para poder reutilizar fragmentos de JSP que se incluyen en numerosas páginas. Si nos fijamos en la aplicación Sp.Shop que acompaña al artículo, podemos ver que todas las páginas siguen un diseño común, y todas contienen una cabecera, un menú (a la izquierda) y un pie de página con el copyright del sitio web. Mediante la directiva “<%@ include %>” podemos incluir en todas nuestras páginas una determinada JSP (por ejemplo, “pie.jsp”) de modo que si en el futuro tenehttp://digital.revistasprofesionales.com


MIDDLEWARE

Struts práctico (II)

LISTADO 4

Plantilla para Sp.Shop

<%@ page contentType=”text/html;charset=ISO-8859-1” language=”java” %> <html> <%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %> <head> <title><tiles:getAsString name=”titulo” ignore=”true”/></title> <link href=”css/forAll.css” rel=”stylesheet” type=”text/css”> </head>

Diseño de la plantilla para Sp.Shop.

mos que modificar dicho pie (modificar el texto de copyright, por ejemplo), sólo lo tendríamos que hacer en una única JSP. Pero eso sólo resuelve parte del problema. Todas las páginas que deban mostrar el pie de página, deben tener cuidado de situarlo en el mismo lugar de la página. ¿Qué ocurre si se toma la decisión de modificar la ubicación en la página de uno de los elementos que hemos añadido, mediante la directiva “include”? No habría más remedio que modificar todas las páginas JSP y cambiar el lugar donde se realiza el “include”. Para resolver este problema, debemos introducir un concepto nuevo ya utilizado en otras tecnologías para el diseño de interfaces de usuario, el de “template” o plantilla y que no existe directamente en la especificación JSP de J2EE. La idea consiste en especificar de manera única, el diseño o layout de un conjunto de páginas. Esta plantilla o layout sería lo único que hubiese que cambiar en caso de querer modificar el diseño común de un conjunto de páginas. Esta plantilla debe maquetar la ubicación de los distintos elementos dinámicos que irán variando de página a página. Las páginas concretas que se mostrarán como resultado de una operación se limitarán a referenciar a una cierta plantilla y asignarán el contenido de las distintas piezas que usa la plantilla usada (cabecera, menú, pie y contenido). Aunque ya hemos comentado que J2EE no dispone de este mecanismo de forma explicita, Struts lo implementa mediante el framework denominado Struts Tiles. Esta solución no está limitada a su uso con Struts y hace tiempo que se generalizó y se puede http://digital.revistasprofesionales.com

<body> <table align=center width=”760” height=”100%” border=”0” cellpadding=”0” cellspacing=”0” background=”images/bg_main.gif”> <tr> <td height=”65”> <tiles:insert attribute=”cabecera”/> </td> </tr> <tr> <td valign=”top”> <table width=”100%” border=”0” cellspacing=”0” cellpadding=”0”> <tr valign=”top”> <tiles:insert attribute=”menu” ignore=”true”/> <tiles:insert attribute=”contenido”/> </tr> </table> </td> </tr> <tr> <td height=”37” background=”images/down_bg.gif”> <tiles:insert attribute=”pie”/> </td> </tr> </table> </body> </html>

usar perfectamente con otros frameworks MVC. Esta solución ha conseguido introducir el concepto de diseño basado en plantillas en el desarrollo J2EE y además otro de sus logros es que su uso es enormemente sencillo, en parte, porque está basado en librerías de etiquetas personalizadas, concepto muy usado en el desarrollo con JSPs.

Plantillas con Struts Tiles Una vez definido el concepto de “plantilla” vamos a pasar a contar cómo definirlas mediante el uso de Struts Tiles. Afortunadamente no tenemos que aprender un nuevo lenguaje o tecnología para diseñar las plantillas, ya que estas se definen en una JSP. Echemos un vistazo al listado 4 donde hemos diseñado la plantilla principal que vamos a usar para nuestra aplicación de ejemplo Sp.Shop. Si observamos la plantilla (WebContent/plantillas/plantillaPrincipal.jsp) no observamos ninguna diferencia con respecto a otras JSPs. Contiene el código HTML necesario para posicionar mediante tablas las cuatro partes (o “tiles” según la terminología de Struts) que ya hemos mencionado anteriormente. Mediante la etiqueta “<tiles:insert>” indicamos que el resultado de la JSP debe insertar la JSP que se indique bajo las variables “cabecera”, “menú”,

“contenido” y “pie”. Mediante el atributo “attribute” indicamos la variable que contendrá el nombre de la JSP que se procesará y cuyo resultado se incluirá en la página. Si opcionalmente usamos “ignore=”true”” indicaremos que en caso de que no se encuentre valor para la variable indicada, no se provocará un error y se seguirá el procesamiento de la JSP sin incluir nada en la página resultante. Si por el contrario usamos la etiqueta “getAsString” indicaremos que directamente se incluirá el valor de la variable indicada (sin tomar el valor como el nombre de una JSP). A continuación veremos cómo se realiza la asignación de estas variables en cada una de las páginas.

Uso de plantillas Una vez definidas las plantillas, sólo nos queda explicar cómo usarlas. En primer lugar, mediante la etiqueta “<tiles:insert>” indicaremos la plantilla que queremos usar (mediante el atributo “page”). A continuación daremos valor a las distintas variables que se definen en dicha plantilla. Esto lo haremos mediante la etiqueta “<tiles:put>”. Veamos la página “WebContent/homeRegistroUsuario.jsp” en el listado 5. Mediante las plantillas, conseguimos el objetivo de no duplicar el diseño y centralizarlo en un único punto (la plantilla). Se 35

SOLO PROGRAMADORES nº 127


MIDDLEWARE

<definition name=”spshop.menu.alt”

LISTADO 5

Contenido del fichero homeRegistroUsuario.jsp

<%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %>

extends=”spshop.default”> <put name=”menu” value=”/arbol2.jsp” />

<tiles:insert page=”/plantillas/plantillaPrincipal.jsp” flush=”true”> <tiles:put name=”titulo” value=”Registro de Usuario” /> <tiles:put name=”cabecera” value=”/head.jsp” /> <tiles:put name=”menu” value=”/arbol.jsp” /> <tiles:put name=”contenido” value=”/registroUsuario.jsp” /> <tiles:put name=”pie” value=”/pie.jsp” /> </tiles:insert>

recomienda al lector comparar este nuevo código con el entregado en el número anterior, donde no se usaba el concepto de plantillas. Antes de pasar a otro punto, vale la pena aclarar que el atributo “flush” indica si se debe vaciar el stream de salida (donde se está escribiendo la respuesta HTTP) antes de procesar la etiqueta. Se recomienda usar siempre “flush=”true””.

Definitions Ya hemos visto cómo adaptar nuestras páginas para que usen el “template” que hemos definido. Si modificamos el resto de páginas enseguida nos daremos cuenta de que todas serán muy similares y que realmente sólo cambia el título y el contenido, ya que el resto de tiles siempre es igual. Para evitar esto Struts Tiles introduce un nuevo concepto, el de definición (“definition”). Una definición consiste en una instanciación de una plantilla con unos valores predefinidos. Es decir, en nuestro caso nos interesaría declarar una definición que asignase unos valores por defecto a las variables, cabecera, menú y pie. También opcionalmente podríamos asignar un titulo por defecto para que todas las páginas mostrasen el mismo. Struts permite que las definiciones se declaren de dos maneras, mediante JSPs (usando la etiqueta “<tiles:definition>”) o mediante ficheros XML. Vamos a ver cómo se haría usando esta última forma. Para poder declarar definiciones mediante XML, necesitamos incluir un plug-in dentro del fichero de configuración de Struts (“strutsconfig.xml”) según se indica a continuación: <plug-in className=”org.apache.struts.tiles. TilesPlugin”> <set-property property=”definitionsconfig” value=”/WEB-INF/tilesdefs.xml” /> <set-property property=”definitionsparser-validate” value=”true” /> </plug-in>

La primera propiedad (“definitions-config”) indica el nombre del fichero donde se buscarán

SOLO PROGRAMADORES nº 127

36

las definiciones declaradas. En el listado 6 podemos ver la forma de declarar una definición (fichero “WebContent/WEB-INF/tilesdefs.xml”). Cualquier página que use esta definición, estará usando la plantilla “plantillaPrincipal.jsp” y no tendrá que asignar valores para las variables “titulo”, “cabecera”, “menu” y “pie”. A pesar de usar una definición, la página puede perfectamente sobrescribir alguno de los valores asignados. Veamos por ejemplo el contenido de la JSP “WebContent/homeVerCesta.jsp” que referencia a la definición que acabamos de declarar: <tiles:insert

</definition>

Internacionalización Otra ventaja que nos ofrece el uso de definiciones es el poder usar distintas definiciones en función del Locale de usuario de la aplicación. Para modificar las dos definiciones que hemos visto para usuarios con el idioma inglés, bastará con crear un fichero con el nombre “WEB-INF/tilesdef_en.xml” (añadir el sufijo del Locale deseado, igual a como se nombran los ficheros “properties” para los ficheros de mensajes internacionalizados). El lector puede consultar este nuevo fichero donde hemos hecho que el titulo por defecto esté en inglés y se utilicen las páginas “arbol2.jsp” para el menú y “pie2.jsp” para el pie de página.

Commons-validator

definition=”spshop.default” flush=”true”> <tiles:put name=”titulo” value=”Cesta de la compra”/> <tiles:put name=”contenido” value=”/verCesta.jsp” /> </tiles:insert>

En el caso anterior, el valor asignado a la variable “titulo”, sobrescribe al título por defecto declarado en la definición. Otra ventaja que nos ofrecen las definiciones es que permiten herencia. Es decir, una definición puede extender otra y así dispondremos de las ventajas que ya conocemos del concepto de herencia. Imaginemos que queremos usar otra definición muy parecida a la que acabamos de ver y queremos que la nueva página tenga un menú diferente (“menu2.jsp”). Para ello indicaremos que la nueva definición herede de la definición “spshop.default” y sobrescribiremos el valor del tile “pie”:

LISTADO 6

Una de las tareas más tediosas dentro del desarrollo de aplicaciones suele ser el tener que realizar las validaciones de los distintos campos de la interfaz de usuario. Si además se trata de una aplicación web, el trabajo es doble, ya que las buenas prácticas nos dicen que la validación tiene que ser tanto en el lado cliente mediante Javascript como en el lado servidor mediante Java (ActionForms si usamos Struts). Esto sin duda supone un problema pues de nuevo nos encontramos en la situación no deseable de tener código duplicado y en distintos lenguajes, lo que dificulta el mantenimiento de las aplicaciones. El proyecto commons-validator de Jakarta trata de resolver este tipo de problemas. Validator es un framework genérico que permite crear validadores de datos a partir de una definición en XML. Usado con frameworks como Struts, esto se traduce en que mediante la declaración en XML de los datos de un forDefinición en el documento tiles-defs.xml

<tiles-definitions> <definition name=”spshop.default” path=”/plantillas/plantillaPrincipal.jsp”> <put name=”titulo” value=”Sp.Shop - La tienda de música en internet” /> <put name=”cabecera” value=”/head.jsp” /> <put name=”menu” value=”/arbol.jsp” /> <put name=”pie” value=”/pie.jsp” /> </definition> </tiles-definitions>

http://digital.revistasprofesionales.com


MIDDLEWARE

mulario, Validator se encargará automáticamente de la validación de nuestros ActionForms (es decir, no tendremos que codificar el método “validate()”) y además, si lo deseamos, puede incluir automáticamente el código Javascript necesario para validar los datos antes de ser enviados al servidor. A continuación vamos a ver cómo utilizar commons-validator para validar los formularios de registro de usuario y de solicitud de pedido en nuestra aplicación Sp.Shop.

Usando commons-validator Como ya hemos contado, mediante el uso de commons-validator no será necesario programar el método “validate()” en las clases ActionForms donde hasta ahora teníamos que

codificar la validación de cada uno de los campos cuando el formulario era enviado al servidor. Pero para que esto sea así, debemos hacer que nuestro ActionForm herede de la clase “org.apache.struts.validator.ValidatorForm” en lugar de “org.apache.struts.action.ActionForm” como hasta ahora. La clase ValidatorForm ya contiene un método “validate()” que se encargará de realizar la validación conforme a la validación que hayamos declarado en los ficheros de configuración. El lector puede ver el código final de los nuevos ActionForms en las clases “DatosRegistroForm” y “DatosPedidoForm”. A continuación pasaremos a declarar cada uno de los formularios que queremos validar mediante commons-validator. Para ello crearemos el fichero “WEB-INF/validations.xml” (con-

sultar dicho fichero en el material complementario de este artículo). Este fichero define dos formularios (“datosRegistro” y “datosPedido”) dentro de la etiqueta “<formset>”. Cada formulario contiene una etiqueta “field” por cada uno de los campos del mismo que queremos validar. El formato genérico para la definición de cada campo es el siguiente: <field property=”nombreDelCampo” depends=”validacion1,…,validacionN”> <arg0 key=”key.mensaje.campo1” /> …… <argN key=”key.mensaje.campoN” /> <var> <var-name>nombreVariable1</var-name> <var-value>valorVariable1</var-value> </var>

Reglas de validación incluidas en Struts Regla

Descripción

…… <var> <var-name>nombreVariableN</var-name>

Required

El campo es obligatorio y debe contener valor.

minlength

El campo debe tener un valor con una longitud mínima de caracteres.

maxlength

El campo debe tener un valor con una longitud máxima de caracteres.

Mask

El valor del campo debe cumplir con una cierta expresión regular.

Byte

El valor del campo debe ser válido para un tipo de dato Byte.

Short

El valor del campo debe ser válido para un tipo de dato Short. El valor del campo debe ser válido para un tipo de dato Integer. El valor del campo debe ser válido para un tipo de dato Long. El valor del campo debe ser válido para un tipo de dato Float. El valor del campo debe ser válido para un tipo de dato Double.

Integer Long Float Double

El valor del campo debe ser válido para un tipo de datos Date, según el patrón de fecha indicado. El valor del entero indicado debe encontrarse en el rango de valores especificados.

Date intRange

El valor del float indicado debe encontrarse en el rango de valores especificados.

floatRange

El valor del campo debe ser un número de tarjeta de crédito válido. El valor del campo debe ser una dirección de correo electrónico válida (sintácticamente). El valor será válido cuando se evalúe a true la expresión indicada.

creditCard Email validwhen

SOLO PROGRAMADORES nº 127

38

<var-value>valorVariableN</var-value> </var> </field>

Podemos observar como cada etiqueta “field” tiene dos atributos obligatorios: “property”, donde indicaremos el nombre del campo al que queremos aplicar la validación (debe corresponderse con el nombre del campo en el ActionForm) y “depends”, donde se indicará la lista de reglas que debe cumplir el valor del campo. Struts implementa una serie de reglas básicas que hemos listado en el cuadro “Reglas de validación incluidas en Struts” (aunque como veremos más adelante, commons-validator permite definir nuestras propias reglas). Cada regla muestra un mensaje localizado que lógicamente queremos personalizar para cada campo y que muestre por ejemplo el campo que no cumple la validación. Para esto se utilizan las etiquetas “<arg0>…<argN>”. Por ejemplo, las reglas “Intrange” y “Floatrange” muestran el mensaje: errors.range=El campo ‘{0}’ debe estar entre {1} y {2}

A la hora de componer el mensaje, Struts sustituirá las cadenas “{0}”,”{1}” y “{2}” por lo que hayamos indicado en las etiquetas “<arg0>”, “<arg1>” y “<arg2>”. Por ejemplo, la siguiente validación provocará el mensaje “El campo “Cantidad” debe estar entre 1 y 5”: http://digital.revistasprofesionales.com


MIDDLEWARE

Struts práctico (II)

<field property=”cantidad” depends=”required,intRange”> <arg0 key=”error.ejemplo.campoCantidad” /> <arg1 key=”1” resource=”false”/> <arg2 key=”5” resource=”false”/> …… </field>

Notar como el valor a sustituir se puede indicar directamente (resource=”false”) o localizado (resource=”true” o sin atributo “resource”) La etiqueta “<var>” nos permite pasar parámetros a los validadores. Imaginamos por ejemplo que queremos que el valor de un campo sólo contenga valores alfabéticos, como por ejemplo el campo nombre del formulario de registro. Para ello tendremos que indicar mediante una variable la expresión regular que debe cumplir el valor del campo: <field property=”nombre” depends=”required,mask”> <arg0 key=”error.registro.faltaNombre” /> <var> <var-name>mask</var-name> <var-value>^[a-zA-Z]*$</var-value> </var> </field>

IE nos informa sobre qué campos son obligatorios.

tor en el fichero de configuración de Struts (“struts-config.xml”). Para ello hay que añadir el siguiente código: <plug-in

Notar en este caso que el atributo “mask” debe indicar una expresión regular siguiendo la sintaxis de Perl5.

Configurar Validator con Struts Para usar commons-validator junto con Struts, debemos seguir los siguientes pasos:  Incluir los ficheros “commons-validator1.1.4.jar” y “jakarta-oro.jar” en el directorio “WEB-INF/Lib”.  Añadir el fichero XML con la definición de las reglas de validación utilizadas (“WEB-INF/validator-rules.xml”). El fichero que el lector encontrará en el material complementario de este artículo, contiene la configuración de las reglas de validación básicas implementadas por Struts y comentadas anteriormente. Más adelante, veremos el formato de este fichero para incluir nuestro propio validador.  Añadir el plugin de commons-validahttp://digital.revistasprofesionales.com

ficacionRegistro.jsp” (que usan el mismo ActionsForm “datosRegistro”) hemos añadido la etiqueta de la siguiente manera:

className=”org.apache.struts.

<html:javascript

validator.ValidatorPlugIn”>

formName=”datosRegistro” />

<set-property property=”pathnames” value=”/WEB-INF/validator rules.xml,/WEB -INF/validations.xml”/> </plug-in>

Habilitar validaciones Javascript Ya sólo nos queda hacer que validator genere automáticamente el código Javascript necesario para validar los campos en el navegador basándose en las validaciones definidas en el fichero “validations.xml”. En primer lugar debemos incluir la etiqueta “<html:javascript>” en la página JSP donde se incluye el formulario. A esta etiqueta le añadiremos el atributo “formName” donde indicaremos el nombre del formulario según esta definido en el fichero “struts-config.xml”. En las páginas “registroUsuario.jsp” y “modi-

Esta etiqueta añadirá una serie de funciones Javascript para realizar las validaciones. Pero a nosotros nos interesa sólo una, que será la que se encargue de validar el formulario completo y que se llamará “validateDatosRegistro” (“validate” seguido del nombre del form indicado en el atributo “formName”). Esta función recibirá un único argumento que será el objeto formulario y tendremos que invocarla antes de enviar el formulario. En el caso de las páginas de registro de usuario, modificaremos el código del botón “aceptar” por el siguiente: <html:img srcKey=”spshop.img.aceptar” bundle=”ui” border=”0” onclick=”if (validateDatosRegistro( document.forms[0])) { document.forms[0].submit()}” />

39

SOLO PROGRAMADORES nº 127


MIDDLEWARE

Crear nuestro propio Validador

depends=””

A continuación vamos a ver lo sencillo que resulta añadir un nuevo validador a commons-validator. El nuevo validador se encargará de comparar el valor del campo al que se aplica, con algún otro campo del formulario (que se informará mediante la variable “segundoCampo”). En el caso de la página de registro de usuario, se comprobará que la contraseña y la confirmación son iguales. En primer lugar crearemos un método estático llamado “compara” en la clase “SpShopValidator”, tal como muestra el listado 7. Como se puede apreciar, resulta muy sencillo y el código es bastante claro. Las clases “ValidatorUtils” y “GenericValidator” nos ayudan a extraer los valores introducidos por el usuario, así como los valores con los que se definió la validación en el XML “validations.xml”. El siguiente paso es dar de alta la nueva regla de validación en el fichero “validatorrules.xml”:

msg=”errors.compara”>

<validator name=”compara” classname=”com.sp.spshop.commons.vali dator.SpShopValidator” method=”compara” methodParams=”java.lang.Object, org.apache.commons.validator.Validator Action, org.apache.commons. validator.Field, org.apache.struts.action. ActionMessages, javax.servlet.http.HttpServletRequest”

LISTADO 7

<javascript> ... </javascript> </validator>

Básicamente se indican el nombre de la regla (“compara”), la clase Java donde se implementa el método, nombre del método y los argumentos que toma de entrada, que normalmente siempre son los cinco indicados. También se indica el texto que se mostrará cuando no se cumpla la validación (clave del mensaje en el fichero “properties”). Por último y de forma opcional, se puede indicar el código Javascript necesario para que la validación se pueda incluir cuando se use la etiqueta “<html:javascript>”. En el fichero “validatorrules.xml” hemos incluido el código Javascript necesario para que se pueda realizar la comparación de campos en el navegador.

Un último apunte sobre commons-validator. En muchos casos las reglas de validación definidas en el fichero “validations.xml” pudieran tener que ser distintas. Imaginemos por ejemplo un caso en el que se tuviese que introducir una fecha mediante una caja de texto. En este caso por ejemplo sería interesante aceptar que el formato de fecha permitido fuese distinto en función del Locale de usuario. La etiqueta “<formset>” nos permite definir validaciones para un determinado idioma o Locale mediante los atributos “country” y Método del validador compara()

// obtenemos el valor del segundo campo String value2 = ValidatorUtils.getValueAsString(bean, sProperty2); // vemos si se asignó algún valor al campo if (!GenericValidator.isBlankOrNull(value)) { // comparamos los valores if (!value.equals(value2)) { // añadimos error errors.add(field.getKey(), Resources.getActionMessage(request, va, field)); // retornamos false para indicar que no se cumplió la validación return false; } // ok, validación correcta return true; }

40

<form-validation> <formset> <form name=”datosRegistro”> <field property=”fecha” depends=”required,date”> <arg0 key=”error.XXXX” /> <var><var-name>datePattern</ var-name><var-value>dd/MM/yyyy</ var-value></var> </field> </form> <formset language=”en”> <form name=”datosRegistro”> <field property=”fecha” depends=”required,date”> <arg0 key=”error.XXXX” /> <var><var-name>datePattern</ var-name><var-value>MM-dd-yyyy</ var-value></var>

i18n con Validator

public static boolean compara(Object bean, ValidatorAction va, Field field, ActionMessages errors, HttpServletRequest request) { // obtenemos el valor del campo String value = ValidatorUtils.getValueAsString(bean, field.getProperty()); // leemos la variable que contiene el nombre del segundo campo a comparar String sProperty2 = field.getVarValue(“segundoCampo”);

SOLO PROGRAMADORES nº 127

“language”. El siguiente ejemplo muestra este caso:

</field> </form>

Nótese que sólo es necesario definir aquellos campos que difieran de las reglas del “formset” por defecto (el que no lleva ni “country” ni “language”).

Instalación de la aplicación Sp.Shop En el material que acompaña a la revista se encuentra el código fuente completo de la aplicación Sp.Shop, así como el instalable de Tomcat 5.5 para Windows y el motor de base de datos HSQLDB. El fichero “Leeme.txt” contiene las instrucciones paso a paso del proceso de instalación.

Conclusiones En este segundo artículo sobre Struts hemos visto cómo mejorar la aplicación Sp.Shop sobre la que estábamos trabajando. Hemos visto las facilidades que nos da Struts para dar soporte multi-idioma a nuestra aplicación, hemos visto cómo diseñar sitios web basándonos en plantillas mediante Struts Tiles y por último hemos visto cómo realizar fácilmente las siempre tediosas validaciones de datos. En la próxima entrega concluiremos nuestro curso sobre Struts explicando más sobre este framework para el desarrollo J2EE. http://digital.revistasprofesionales.com


REDES

Creación de aplicaciones web con ColdFusion MX 7 (II) RICARDO DÍEZ FERREIRA

ColdFusion proporciona multitud de funciones para el tratamiento de datos, fechas, operaciones matemáticas o información del sistema y sus recursos. Introducción En la primera entrega de la serie se introdujeron las ventajas que proporciona ColdFusion para el desarrollo web, a lo que hay que añadir una gran sencillez en el aprendizaje y una gran rapidez en los tiempos de desarrollo. Se mostró el uso de las principales etiquetas del lenguaje de programación de ColdFusion (CFML), además de ejemplos de acceso a bases de datos, y de las principales tareas en programación web. En esta segunda entrega se mostrará la utilidad de las funciones predefinidas que proporciona el lenguaje, las facilidades para el tratamiento de XML, XSL y servicios web, las diferentes formas de capturar los errores en ColdFusion, y varios ejemplos mostrando la utilización de la mayoría de herramientas del lenguaje vistas hasta el momento.

Sitio web de ColdFusion Developer Center, donde se pueden encontrar multitud de ejemplos y artículos: http://www.macromedia.com/devnet/mx/coldfusion/.

SOLO PROGRAMADORES nº 127

42

Las funciones en ColdFusion ColdFusion, además de las etiquetas presentadas en la anterior entrega, proporciona multitud de funciones predefinidas que hacen más sencilla la manipulación de datos. Existen muchísimas y muy variadas, y se pueden dividir en distintas categorías de funciones. Funciones de manipulación de cadenas de texto, que facilitan el parseo, la comparación y la conversión de cadenas de texto. Ejemplos de estas funciones son: “Compare”, “Find”, “LCase”, “Insert”, “LTrim”, “Replace”, etc. Las funciones de fecha y hora permiten tratar este tipo de datos, hacer comparaciones de fechas, etc. ColdFusion tiene un objeto fecha/hora que es una representación interna de esa fecha, que no está diseñada para ser mostrada sin más. Se deberán usar las funciones de formato de fecha/hora para poder mostrar estos datos correctamente. Ejemplos de este tipo de funciones son: “CreateDate”, “CreateTime”, “Date Compare”, “DayOfWeek”, “isDate”, “Now”, “Date Format”, etc. Existen también funciones matemáticas que permiten realizar cálculos complejos, como “Abs”, “Rand”, “Cos”, “Log”, etc. Las funciones para el tratamiento de listas y arrays permiten tratar los elementos de este tipo de variables. Con las funciones de sistema, se puede leer y escribir en ficheros, directorios, y recuperar los datos del entorno de ejecución. Ejemplos de este tipo son: “FileExists”, “DirectoryExists”, “Get Locale”, etc. Y en general, hay funciones para solucionar todo tipo de problemas, como funciones de tratamiento XML, de búsqueda en textos, de evaluación dinámica, etc. En definitiva, las funciones predefinidas de ColdFusion ayudan al desarrollador con las tareas más comunes de la manipulación de datos, dando lugar a programas más comprensibles, menos extensos y que se realizan en menos tiempo. A continuación, se van a mostrar sencillos ejemplos de algunas de las funciones más utilizadas y de mayor interés. http://digital.revistasprofesionales.com


REDES

Creación de aplicaciones web con ColdFusion MX 7 (II)

Funciones de cadenas de texto El tratamiento cómodo de cadenas de texto es necesario en cualquier lenguaje que se precie. Esta comodidad hace que lenguajes como Perl sean muy populares, principalmente por proporcionar la potencia de las expresiones regulares. ColdFusion lo hace con las funciones de tratamiento de cadenas de caracteres. De ellas, las fundamentales son: 











F i n d ( s u b c a d e n a , c a d e n a , p o s i c i ó n ): Es una función que busca el contenido de “subcadena” en “cadena”, comenzando en el carácter indicado por “posición”. La posición es un parámetro optativo. R e p l a c e ( c a d e n a , s u b c a d e n a 1 , s u bc a d e n a 2 , á m b i t o ): Esta función sustituye en “cadena” el valor de “subcadena1” por “subcadena2”. El parámetro “ámbito” es opcional y puede ser “one”, para sólo sustituir una ocurrencia, o “all”, para sustituir todas. Compare(cadena1, cadena2): Compara la “cadena1” con la “cadena2”, devolviendo –1 si “cadena1” es alfabéticamente menor, 0 si son iguales, y 1 si es alfabéticamente mayor. L c a s e ( c a d e n a ): Devolverá la cadena con letras minúsculas. La función “Ucase(cadena)” realiza la operación contraria. I n s e r t ( s u b c a d e n a , c a d e n a , p o s ic i ó n ): Esta función introducirá la “subcadena” dentro de la “cadena” a partir de la “posición” indicada, desplazando los elementos que estuvieran actualmente en esa “posición”. T r i m ( c a d e n a ): Elimina todos los espacios sobrantes al inicio y al final de la “cadena”. Esta función viene muy bien para cuando un usuario ha introducido espacios innecesarios en los campos de un formulario.

Funciones de fecha En todos los lenguajes de programación el tratamiento de las fechas es un tema de vital importancia, y en algunos de ellos puede llegar a ser realmente pesado tratar con ellas. En ColdFusion las funciones de fecha/hora hacen esta labor muy llevadera. Las principales funciones de esta categoría son:  N o w ( ) : Es una función que devuelve la fecha y hora actuales del sistema.  CreateDateTime(año,mes,dia,hora, m i n u t o , s e g u n d o ) : Crea un nuevo objeto fecha/hora con los parámetros que se le pasen.  D a t e F o r m a t ( f e c h a , m a s c a r a ) : Sirve http://digital.revistasprofesionales.com

LISTADO 1

Practicando con las funciones de fecha

<cfset hoy = Now()> <cfset cumple = CreateDateTime(2005, 10, 11, 0, 0, 0)> <html> <body> Ejemplo de funciones de fecha<br><br> <cfoutput> <ul> <li>La fecha de hoy es: #hoy# <li>Usando DateFormat sin mascara: #DateFormat(hoy)# <li>Usando DateFormat con mascara: #DateFormat(hoy, “dd-mm-yyyy”)# <cfif DateCompare(hoy, cumple, “d”) eq 0> Hoy es mi cumpleaños. </cfif> </ul> </cfoutput> </body> </html>

para poder mostrar un objeto fecha de una manera concreta. La máscara será del estilo “dd/mm/yyyy” o similar.  DateCompare(fecha1,fecha2, p r e c i s i o n ) : Esta es la función de comparación de fechas, que devolverá -1, 0 o 1, dependiendo de si “fecha1” es menor, igual o mayor que “fecha2” respectivamente. El dato de precisión es opcional y permite indicar si se quiere afinar la comparación a segundos, minutos, horas, días, etc. Con estas pocas funciones, se pueden realizar la mayoría de labores de tratamiento de fechas. De todas formas, existen multitud de funciones más para el tratamiento de fechas, como “DateDiff”, “DateConvert”, “DayOfWeek”, “DayOfYear”, que ayudan con el resto de labores menos usuales en la programación.

Funciones matemáticas Este grupo de funciones sirven para realizar todo tipo de cálculos matemáticos, aparte de las sumas, restas, multiplicaciones y divisiones, que se realizan mediante operadores y que ya fueron explicadas en la primera entrega de la serie. A modo de ejemplo, se van a mostrar unas cuantas:

 





A b s ( n ú m e r o ) : Esta función devolverá el “número” sin signo. RandRange(número1, número2): El resultado será un número aleatorio entre el “número1” y el “número2”. C o s ( á n g u l o ) : En este caso se conseguirá el valor del coseno del “ángulo” indicado en radianes. L o g ( n ú m e r o ) : Esta función devuelve el logaritmo del “número” indicado.

Funciones de sistema Como ya se ha indicado, las funciones de sistema de ColdFusion ayudan al tratamiento de ficheros y directorios, así como a recuperar o modificar información relevante del entorno de ejecución del servidor. Algunas de las más utilizadas son las siguientes: “FileExists”, “DirectoryExists” y “GetLocale”:  F i l e E x i s t s ( p a t h ): Devolverá verdadero si el fichero indicado en el “path” absoluto existe. Hay una función similar para directorios llamada “DirectoryExists(path)”.  G e t L o c a l e ( ): El resultado será el valor del lenguaje y zona geográfica del entorno de ejecución.  G e t E n c o d i n g ( á m b i t o ): Esta función devuelve el tipo de codificación utilizada en el formulario o la URL (UTF-8, ISO-8859-1, etc). El valor de “ámbito” puede ser FORM o URL.  s e t E n c o d i n g ( á m b i t o , c o d i f i c ac i ó n ): En este caso cambiará el tipo de “codificación” en el “ámbito” elegido (FORM o URL). El valor de codificación puede ser UTF-8, US-ASCII, etc.

Otras funciones interesantes En la ayuda que se instala junto con la aplicación, se pueden ver ejemplos de todas las funciones predefinidas.

Hay muchas más funciones en ColdFusion que no pertenecen a ninguno de los tipos anteriores, pero que también son interesantes por lo que aportan al desarrollador. 43

SOLO PROGRAMADORES nº 127


REDES



E n c r y p t ( c a d e n a , c l a v e ): Esta función devuelve la “cadena” de texto encriptada utilizando la “clave” que se proporciona. Se realiza con un algoritmo simétrico por clave. La función “Decrypt(cadena,clave)” desencripta la cadena de texto anteriormente encriptada. A continuación se muestra un breve ejemplo de su uso: <cfset cadena = “Mi perro se llama copi”> <cfset clave = “mipassword”> <cfset txt_encriptado = encrypt (cadena, clave)> <cfset txt_desencriptado = decrypt(txt_encriptado, clave)>







UrlEncodedFormat(cadena): Convierte una “cadena” de texto en otra que se pueda usar para generar una URL. Sustituye los espacios en blanco por “%20”, y los acentos por su código respectivo. Sirve para poder enviar cualquier parámetro que contenga este tipo de caracteres en una llamada “GET”. La función “UrlDecode(cadena)” realiza la operación contraria. A r r a y T o L i s t ( a r r a y ) : Convierte un “array” unidimensional en una lista de valores. Evidentemente, la función “ListToArray(lista)” hace la operación inversa. L i s t S o r t ( l i s t a , t i p o _ o r d e n , s e n t id o _ o r d e n , d e l i m i t a d o r ): Esta función ordena todos los elementos de una “lista”. Para ello recibe como parámetros la “lista” a ordenar, el tipo de orden, que puede ser “numeric”, “text” o “textnocase”, que serían orden numérico, alfabético, o alfabético sin tener en cuenta mayúsculas y minúsculas. También se debe introducir el sentido del orden, que puede ser “asc” o “desc”, y finalmente el “delimitador” de la lista, que es opcional e indica cuál es el carácter de separación entre los elementos (coma por defecto).

Tratamiento de XML con ColdFusion

Arquitectura general de la implementación de servicios web con ColdFusion.

Como se ha adelantado en la introducción, ColdFusion también incluye ciertas funcionalidades para facilitar el tratamiento de XML permitiendo, en muy pocas líneas, hacer cosas que suponen bastante trabajo en otros lenguajes, como el parseo de documentos, o la transformación de XML con XSLT, así como la utilización de servicios web. Todo esto se hace mediante una etiqueta y varias funciones predefinidas. Las fundamentales son las siguientes:  < c f x m l v a r i a b l e = ” n o m b r e _ o b j et o ” >: Esta etiqueta crea un nuevo objeto “documento” de ColdFusion. Todo lo que haya entre la apertura de la etiqueta (“<cfxml>”) y el cierre de la misma (“</cfxml>”), se convertirá en el objeto “documento”. En el siguiente ejemplo se puede ver cómo funciona esta etiqueta:



<cffile action=”read” file= ”C:\familia.xml” variable=”familia”> <cfset documento=XmlParse(familia)>



<cfxml variable="documento"> <familia> <marido>Pepe</marido> <mujer>Luisa</mujer> <cfloop index = "i" from = "1" to = "3"> <hijo>

SOLO PROGRAMADORES nº 127

44

Lo mismo podría hacerse con la respuesta de una llamada HTTP a un servicio web, utilizando la etiqueta “<cfhttp>”. X m l T r a n s f o r m ( X M L , X S L T ) : Esta función le aplica una hoja de estilo XSLT al XML, realizando la transformación, y devolviendo el resultado en una cadena de texto. El parámetro “XML” puede ser una cadena que contenga el XML o un objeto “documento”. El parámetro “XSLT” sólo puede ser una cadena de texto. A continuación, se muestra un ejemplo de una transformación, suponiendo que tenemos una hoja de estilo XSLT escrita para el XML anterior:

Hijo numero

<cffile action=”read” file=

<cfoutput>#i#</cfoutput>

”C:\familia.xml” variable=”familia”>

</hijo>

<cffile action=”read” file=

</cfloop>

”C:\familia.xsl” variable=”hoja_estilo”>

</familia>

<cfset resultado = XmlTransform (familia, hoja_estilo)>

</cfxml>

Hoy en día, con el auge de los servicios web, el formato XML ha pasado a utilizarse de forma masiva en todo tipo de aplicaciones como herramienta para representar información.

X m l P a r s e ( c a d e n a X M L ): Esta función parsea el valor de la cadena de texto, que contiene el XML, devolviendo un objeto “documento”. En el siguiente ejemplo, suponiendo que tenemos el mismo XML del ejemplo anterior en un fichero llamado “familia.xml”, se puede leer el fichero y convertirlo en un objeto “documento”:

Como se puede observar en el ejemplo, se pueden introducir otras etiquetas como “<cfloop>”, dentro de la etiqueta “<cfxml>”, para poder introducir lógica en la creación del documento XML.

<cfoutput>#resultado#</cfoutput> 

XmlSearch(objeto_documento, e x p r e s i o n _ x p a t h ): Esta función permite hacer búsquedas dentro del objeto

http://digital.revistasprofesionales.com


REDES

Creación de aplicaciones web con ColdFusion MX 7 (II)

“documento” al estilo de XPath, que trata el XML como si fueran directorios. Devolverá un array con todos los elementos del documento que se encuentren. En el siguiente ejemplo, se muestra cómo se buscarían todos los elementos “hijo” de un XML como los anteriores:

Se ha producido un error:<br> <cfoutput>#cfcatch.message#<br><br> Error de tipo #cfcatch.type# </cfoutput> </cfcatch> </cftry>

LISTADO 2

<cffile action=”read” file= ”c:\familia.xml” variable=”familia”> <cfset documento=XmlParse(familia)>

Contenido de la página error.cfm

<html> <body> Se ha producido un error:<br> #error.diagnostics#<br><br>

<cfset hijos = XmlSearch(documento, “/familia/hijo”)> <cfloop from=”1” to=”#ArrayLen(hijos)#” index=”i”>

Error producido a las #error.dateTime#<br> IP: #error.remoteAddress# Navegador: #error.browser# </body> </html>

<cfoutput>#hijos[i].xmlText# </cfoutput></cfloop> 

ToString(objeto_documento): Como indica su nombre, esta función permite convertir un objeto “documento” en la cadena de texto con el XML. Como se puede comprobar en los ejemplos anteriores, con estas etiquetas o funciones se pueden realizar, rápida y limpiamente, operaciones que en otros lenguajes suponen la utilización de complicados parsers y muchas líneas de código. Por lo tanto, utilizando ColdFusion el programador se abstrae de todos los detalles de la implementación. Además de todas estas funciones, también se proporcionan herramientas para crear servicios web. En el administrador de ColdFusion existe un apartado para la publicación de servicios web, donde se publican los ficheros WSDL para que se pueda acceder al servicio web en el servidor.

Tratamiento de errores Los errores en ColdFusion pueden capturarse y tratarse de una manera que recuerda mucho a Java. Existen varias etiquetas creadas para esta labor, y las más importantes son “<cftry>” y “<cfcatch>”, pero existen algunas más. A continuación se detalla el uso de estas dos etiquetas: <cftry> <!—-A continuación el código a controlar—-> <cfif … <!—-Final del código a controlar—-> <cfcatch type = “Any”>

http://digital.revistasprofesionales.com

En el ejemplo se puede observar que la etiqueta “<cfcatch>” va siempre dentro de “<cftry>”, y que tiene una propiedad llamada “type”, que en este ejemplo tiene como valor “Any”. Esta propiedad sirve para indicar el tipo de error que se quiere capturar, y en este caso, sería de cualquier tipo. Hay muchos otros posibles valores de esta propiedad como por ejemplo “Database” para errores de base de datos, “Application” para errores de aplicación o “Security” para errores de seguridad. También

LISTADO 3

En el administrador de ColdFusion hay una sección donde se pueden publicar los servicios web.

puede observarse que se devuelven dos variables conteniendo: el mensaje de error y el tipo del error. En principio se pueden poner todas las etiquetas “<cfcatch>” que se deseen dentro de un único “<cftry>”. Existe otra etiqueta llamada “<cferror>”, que permite mostrar una página común cuando se produzca un error. En el siguiente ejemplo se muestra cómo introducir la etiqueta indicando en sus propiedades que redirija a la página “error.cfm”, que será la que muestre el error: <cferror type = “request” template = “error.cfm” mailTo = “admin@prueba.com”>

Mostrando los datos de los empleados

<cftry> <cfquery name=”empleados” datasource=”cfdocexamples”> SELECT FirstName, LastName, EMail, Phone FROM Employees ORDER BY FirstName </cfquery> <cfcatch type = “Database”> Se ha producido un error en la consulta a la base de datos. </cfcatch> </cftry> <cfif empleados.recordcount gt 0> <cfset hoy=Now()> <cfxml variable=”documento”> <plantilla> <fecha>#DateFormat(hoy, “dd-mm-yyyy”)#</fecha> <num_empleados>#empleados.recordcount#</num_empleados> <cfoutput query=”empleados”> <empleado> <nombre>#FirstName#</nombre> <apellido>#LastName#</apellido> <email>#email#</email> <telefono>#Phone#</telefono> </empleado> </cfoutput> </plantilla> </cfxml> <cfif FileExists(“c:\empleados.xsl”)> <cffile action=”read” variable=”xsl” file=”c:\empleados.xsl”> <cfset transformacion = XMLTransform(documento, xsl)> <cfoutput> #transformacion# </cfoutput> <cfelse> <cfoutput> #ToString(documento)# </cfoutput> </cfif> </cfif>

45

SOLO PROGRAMADORES nº 127


REDES

A continuación, en el listado 2, se muestra el código que puede tener la página “error.cfm”. Como se puede ver en dicho listado, la página “error.cfm”, que es la que muestra el error, recibe una serie de variables con datos genéricos del error como el mensaje, la fecha, el navegador del usuario, etc. De todas formas, esta última opción es menos aconsejable que la de “<cftry>” y “<cfcatch”>, que proporciona un manejo más flexible de los errores.

Una aplicación sencilla A continuación se va a realizar una aplicación sencilla para ilustrar algunos de los conceptos explicados a lo largo de esta entrega, como el control de errores, el tratamiento de XML y el uso de funciones predefinidas de ColdFusion. También se utilizarán algunas de las etiquetas explicadas en la primera entrega, como las de control de flujo como “<cfif>” o de salida de datos como “<cfoutput>”. En la primera entrega ya se utilizó una base de datos Access que proporciona ColdFusion en

Esta figura muestra el resultado de ejecutar nuestro pequeño ejemplo.

SOLO PROGRAMADORES nº 127

46

LISTADO 4

Contenido del fichero empleados.xsl

<?xml version=”1.0”?> <xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0” xmlns=http://www.w3.org/TR/REC-html40 xmlns:xlink=”http://www.w3.org/1999/xlink”> <xsl:template match=”/”> <html> <head> <title>Empleados</title> </head> <body> Informe de plantilla a <xsl:value-of select=”/plantilla/fecha”/><br/> Numero de empleados: <xsl:value-of select=”/plantilla/num_empleados”/> <table border=”1” width=”350”> <tr> <td>Nombre</td> <td>Apellido</td> <td>Email</td> <td>Telefono</td> </tr> <xsl:for-each select=”/plantilla/empleado”> <tr> <td align=”center”><xsl:value-of select=”nombre”/></td> <td align=”center”><xsl:value-of select=”apellido”/></td> <td align=”center”><xsl:value-of select=”email”/></td> <td align=”center”><xsl:value-of select=”telefono”/></td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>

la instalación para sus ejemplos, y que se llama “cfdocexamples”. En este caso, la volveremos a utilizar. Nuestra aplicación de ejemplo hace una consulta de los datos de los empleados de una compañía en la tabla “Employees”, sacando su nombre, apellido, email y teléfono y genera un documento XML con ello. A continuación, mediante una hoja de estilo XSLT, realiza la transformación mostrándola finalmente al usuario. El listado 3 muestra la página .cfm que realiza todas estas operaciones. Como se observa en el código del listado, se realiza un control de los errores de tipo “Database”, lo que nos permitirá mostrar un mensaje explicando el error en caso de que falle la consulta a la base de datos. En el código anterior se comprueba la existencia y posteriormente se abre un fichero de hoja de estilo XSLT llamado “empleados.xsl”. Este fichero puede tener un formato como el que luce el listado 4. A la vista del ejemplo, se observa que el uso de las etiquetas es muy intuitivo, y que una aplicación como esta, en cualquier otro lenguaje, hubiera sido bastante más extensa, principalmente en la transformación del fichero XML con la hoja de estilo, que en ColdFusion es muy clara y sencilla. Además, como ya se comentó en la primera entrega, la etiqueta “<cfquery>” permite abstraerse totalmente de la implementación del acceso a la base de datos.

Conclusiones En esta segunda entrega se ha profundizado un poco más en las herramientas que ColdFusion pone en manos del programador, para que pueda programar más rápido y de forma más sencilla. Una ayuda fundamental son las funciones predefinidas, de las cuales se han mostrado las principales de cada tipo, y se ha podido observar la potencia de algunas de ellas. Los ejemplos han ilustrado la sencillez del uso de estas funciones. El tratamiento de documentos XML es muy potente en este lenguaje, permitiendo realizar operaciones complejas con etiquetas y funciones predefinidas. La utilización de servicios web (SOAP, WDSL, etc) también es sencilla de desarrollar en ColdFusion, pero queda fuera del objeto de este artículo. Finalmente, se han visto las diferentes formas en que se pueden tratar los errores en este lenguaje de una manera sencilla y que permite generar los mensajes de salida adecuados dependiendo del tipo de error que se haya producido. En la siguiente entrega se explicarán los conceptos más avanzados de Coldfusion, como la creación de etiquetas o funciones propias, la compatibilidad con otros lenguajes de programación, como por ejemplo Java, y los principales temas relativos a la administración del servidor de ColdFusion, para intentar maximizar su rendimiento, sacándole todo el partido posible. http://digital.revistasprofesionales.com


DISEÑO

Generación de código a partir de modelos con EMF OSCAR COMBARROS, IRENE JIMÉNEZ

EMF es un plug-in Open Source de Eclipse que permite la generación automática de código Java a partir de un modelo (UML, XMI, XSD...). Además genera el código necesario para crear editores integrados en la plataforma Eclipse. Introducción Model Driven Architecture Como muchos lectores sabrán, OMG (Object Management Group, http://www.omg.org) es una organización internacional sin ánimo de lucro que engloba a prácticamente todas las grandes empresas de desarrollo de software, y a cientos de pequeñas empresas. Su objetivo es el desarrollo de especificaciones estándar para mejorar la interoperabilidad entre sistemas; una de las más conocidas actualmente es MDA (Model Driven Architecture, http://www.omg. org/mda/). MDA engloba una serie de especificaciones de modelado como son MOF (Meta Object Facility), UML (Unified Modeling Language), XMI (XML Metadata Interchange) y CWM (Common Warehouse Model). La arquitectura MDA permite definir, en base a los estándares citados anteriormente, modelos de negocio independientes de la plataforma, separando así la lógica de negocio de la tecnología en la que se soporta. Por lo tanto, una vez construido el modelo de nuestro software, debería ser posible generar el código para cualquier plataforma conocida, como por ejemplo Java o .NET.

Eclipse Modeling Framework Por su parte, EMF (Eclipse Modeling Framework, http://www.eclipse.org/emf/) es un framework Open Source para la construcción automática de código a partir de modelos. EMF es, por lo tanto, una implementación de MDA para la plataforma Java que permite trabajar con la mayoría de las especificaciones englobadas dentro de MDA. EMF Proporciona su propio metamodelo (ecore)

SOLO PROGRAMADORES nº 127

48

para describir los modelos de datos UML de las aplicaciones. Dispone de un serializador XMI como mecanismo de persistencia de los modelos, herramientas para transformar modelos representados en UML, XML o interfaces Java en metamodelos ecore así como herramientas de generación de código a partir del modelo ecore. Por tanto, EMF y MDA comparten el mismo principio fundamental, que es el de generar implementaciones ejecutables a partir de modelos lógicos. EMF incorpora muchos de los conceptos y estándares de MDA, como es el uso de un modelo en UML como entrada para el desarrollo automático de código, el uso de XMI como formato intermedio para los datos de modelos y metamodelos, y una implementación de su modelo de objetos basada en el un subconjunto del estándar MOF, denominado EMOF (Essential MOF). La finalidad de EMF, como la de cualquier otra implementación de la arquitectura MDA, es la de automatizar ciertas tareas asociadas al desarrollo de aplicaciones acortando por tanto los tiempos dedicados a dicha fase del ciclo de vida de las aplicaciones. Asimismo, como ya se ha mencionado con anterioridad, es posible obtener diferentes implementaciones de un mismo modelo de negocio para tantas plataformas tecnológicas como se desee. En este artículo, el lector va a descubrir cómo, partiendo de un modelo en UML consistente en un conjunto de clases de negocio relacionadas, EMF genera un modelo de objetos que lo implementan. EMF se encarga además de gestionar la persistencia de los objetos del modelo en documentos XML. Una vez que EMF ha generado una implementación Java del modelo, el equipo de desarrollo puede centrarse en implementar la lógica propia del negocio. El motor de generación de código de EMF, JET, puede producir cualquier tipo de contenido en modo texto. En la actualidad se usa para generar código Java, ficheros XML o ficheros de propiedades, pero es posible realizar modificaciones sobre las plantillas usadas por JET para generar código para otros lenguajes de programación.

Generación de código con EMF En el presente artículo haremos una introducción a EMF mediante un ejemplo práctico, veremos cómo instalarlo y las posibilidades que tiene

http://digital.revistasprofesionales.com


DISEÑO

Generación de código a partir de modelos con EMF

Figura 1. El modelo EMF.

para el desarrollo automático de código a partir de un modelo. Eclipse no es sólo un entorno de programación, sino que está diseñado como una plataforma de desarrollo que permite gestionar recursos y manejar programas Java, y que puede ampliarse mediante unidades de ejecución denominadas “plug-ins”. En la actualidad existen plug-ins disponibles para Eclipse que permiten hacer uso de una gran variedad de funcionalidades, como editar documentos XML, desplegar EJBs en los servidores más comunes, desarrollar aplicaciones J2EE, modelar con UML o diseñar interfaces gráficos de modo visual mediante el plug-in VE (véase Sólo Programadores 122). EMF es un plug-in que amplía la capacidad de la plataforma Eclipse, en este caso generando editores que nos permiten manejar de forma sencilla los objetos del modelo.

Instalación de EMF La instalación de EMF es muy simple, sólo tenemos que copiar los ficheros del plug-in en los directorios “plugins” y “features” de Eclipse. EMF se puede obtener de la dirección http://download.eclipse.org/tools/emf/scripts/ downloads.php, aunque también se ha incluido una copia en el CD-ROM. Una vez obtenidos el fichero “emf-sdo-xsdSDK-2.0.2.zip” (de la web o del CD-ROM) basta con descomprimirlo y copiar el contenido del directorio “features” bajo el directorio “features” de Eclipse y el contenido del directorio “plugins” bajo el directorio “plugins” de Eclipse. El paquete de EMF Runtime incluye el generador EMF y un conjunto de plug-ins relacionados. Tras instalar el paquete, habrá que comprobar que están disponibles en el entorno Eclipse. Esto se puede hacer acce-

http://digital.revistasprofesionales.com

diendo a la opción de información de la plataforma Eclipse (About Eclipse Platform), para ello pulsaremos en el menú “Help” -> “About Eclipse Platform”. Una vez allí hacemos clic en “Plug-in Details” y comprobamos que el conjunto de plug-ins propios de EMF están presentes, nos deberían aparecer entradas como “org.eclipse.emf.ecore”, “org.eclipse.emf.codegen” y “org.eclipse.emf.edit”.

¿Qué es EMF? EMF es un entorno de trabajo que permite modelar y generar código a partir de un modelo, pudiéndose construir herramientas y aplicaciones basadas en modelos de datos estructurados. EMF soporta el uso de la especificación XMI (XML Metadata Interchange) como modo de representación y serialización de modelos y metamodelos. EMF es muy genérico permitiendo importar modelos desde diversas fuentes, como esquemas XSD, documentos UML y modelos especificados mediante anotaciones en código Java. También es posible crear un modelo desde cero. EMF se divide en tres módulos principales:  E M F: El core incluye un metamodelo (ecore) con el que se describen los modelos y características de soporte en tiempo de ejecución que incluyen notificación de cambios en el modelo, persistencia en documentos con formato

XML y un API para la manipulación de objetos EMF.  E M F . E d i t : El entorno de trabajo EMF.Edit incluye clases reusables con las que construir editores de modelos EMF. Proporciona clases para la visualización de contenidos, etiquetas y propiedades, que permiten mostrar los modelos EMF mediante vistas y hojas de propiedades. Asimismo, incluye un entorno de comandos genéricos que pueden ser incluidos en editores.  E M F . C o d e g e n: Este módulo de generación de código es capaz de proporcionar las clases necesarias para construir un editor visual para un modelo EMF determinado. Para cada objeto del modelo, EMF generará una serie de clases que se pueden estructurar en 3 paquetes, (Modelo, Adaptadores y Editor):  M o d e l: Son el conjunto de clases e interfaces que representan nuestro modelo. Para cada una de nuestras clases definidas en el modelo, EMF genera un interfaz y una clase que lo implementa.  A d a p t e r s: Son una serie de clases que permiten separar la interfaz gráfica del modelo de negocio. Por cada una de nuestras clases del modelo se genera un adaptador (itemProviders), que dicta cómo se representa gráficamente cada clase e indica cuáles de sus atributos son editables.  E d i t o r : EMF construye de manera opcional dentro de Eclipse un editor estructurado que nos permite modificar de forma gráfica nuestro modelo. La comunicación entre los diferentes paquetes generados por EMF se realiza mediante tres patrones de diseño básicos, Adapter, Command y Observer como se puede ver en la figura 2.

Ejemplo práctico En nuestro ejemplo vamos a modelar un conjunto de clases que representan la estructura de una base de datos. Para ello nos creamos las siguientes clases en UML (véase la figura 3):

Figura 2. Relación entre paquetes.

49

SOLO PROGRAMADORES nº 127


DISEÑO



P r o v e e d o r: La empresa proveedora de la base de datos.  T i p o s D e D a t o s: Representa el conjunto de tipos de datos soportados por las bases de datos del proveedor en cuestión.  T i p o: Un tipo de datos específico, como puede ser “CLOB”, “SMALLINT” o “VARCHAR”.  B a s e D e D a t o s: Una determinada base de datos.  T a b l a: Con esta clase representamos una tabla de la base de datos.  C o l u m n a: Columna de la tabla. Cada columna tiene un tipo determinado de entre los definidos como objetos de la clase Tipo. EMF soporta la utilización de los elementos más comunes de un diagrama de clases en UML, como son clases, atributos y las asociaciones. Cabe destacar que EMF requiere que el modelo de clases obtenido mediante Rational cumpla ciertas condiciones. Las asociaciones entre clases deben ser navegables si queremos que éstas sean reconocidas por EMF. Por ejemplo, si no estableciésemos una asociación navegable entre “Tabla” y “Columna”, EMF no nos facilitaría la opción de crear objetos “Columna” para una “Tabla”. Por otro lado, es necesario definir las asociaciones entre clases del tipo composición siempre que queramos asociar todas las instancias de la clase que es destino de la asociación con el tiempo de vida de ésta. Las asociaciones de agregación o asociación son transformadas por EMF en una propiedad de la clase origen de la asociación. Una vez creado el modelo del ejemplo en UML como se muestra en la figura 3, lo importamos en el entorno Eclipse. Para ello seleccionamos “File” -> “New” -> “Project” y, dentro de la carpeta “Eclipse Modeling Framework”, seleccionamos “EMF Project”, dando por nombre a nuestro ejemplo “EjemploEMF”, y pulsamos “Next”. Seleccionamos la opción “Load from a Rose class model” y hacemos clic en “Next”. En este momento debemos especificar la ruta al fichero con el modelo generado por la herramienta Rational, cuya extensión es .mdl (el fichero con el modelo se ha incluido en el material complementario de este artículo, y lleva por nombre “bd.mdl”) y pulsamos “Next”. En este paso se indica el nombre del modelo generado por EMF,

LISTADO 1

Figura 3. Nuestro modelo.

siendo en nuestro caso “bd.genmodel”. Seleccionamos el paquete raíz y pulsamos “Finish”. Una vez finalizado el proceso de creación del proyecto, podemos inspeccionar su contenido, encontrando dos ficheros “bd.ecore” y “bd.genmodel” dentro de la jerarquía de carpetas “src/model”, tal y como se puede observar en la figura 4. Como hemos indicado con anterioridad, es posible generar un modelo ecore a partir de un documento vacío, mediante el editor que EMF proporciona a tal fin. El fichero “bd.ecore” representa, mediante un documento en formato ecore basado en estándares de OMG, el modelo de negocio que hemos definido en Rational. Dicho fichero puede ser modificado con el editor específico proporcionado por EMF o con cualquier otro editor, al tratarse de un fichero en formato XML. Todos los elementos del modelo se representan mediante nodos. En el ejemplo, la representación de la clase “BaseDeDatos” mediante este lenguaje es la que se muestra en el listado 1. En el fragmento de código del listado aparecen varios nodos correspondientes a diferentes elementos dentro de un modelo ecore. Estos nodos son “eClassifiers” y “eStructuralFeatures” que permiten definir clases, enumeraciones y tipos de datos los primeros, así como atributos y relaciones los segundos. El fichero “bd.genmodel” no especifica cómo es el modelo, sino cómo será generado el código

fuente a partir de éste. Contiene información relativa a la plataforma específica para la que se genera el código. Al igual que sucede con los ficheros .ecore, EMF proporciona editores para la visualización y modificación de ficheros .genmodel. Visto de otra forma, el modelo generación (.genmodel) añade al modelo de negocio (.ecore) un conjunto de atributos específicos para la generación de plug-ins para Eclipse de modo que, si fuese necesario obtener otra implementación del modelo de negocio (.ecore) bastaría con modificar el documento de definición del modelo de generación (.genmodel), nunca el documento ecore. Una vez obtenidos estos ficheros, procedemos a generar el código correspondiente a las clases del modelo y los editores que nos permitirán crear implementaciones de éste. Para ello editamos el fichero “bd.genmodel” con el editor específico de EMF, EMF Generator, hacemos clic con el botón derecho del ratón sobre el nodo raíz del árbol y seleccionamos la opción “Generate All” en el menú contextual. En la figura 4 se puede ver el menú de generación del modelo con la opción “Generate All”. EMF necesita de los modelos ecore y genmodel para obtener el código fuente, además de un conjunto de plantillas denominadas Java Emitter Templates (JET), con una sintaxis similar a la de las páginas JSP. Es posible configurar el proceso de generación modificando el modelo generador o estas plantillas.

Fragmento del fichero bd.ecore relativo a la clase BaseDeDatos

<eClassifiers xsi:type=”ecore:EClass” name=”BaseDeDatos”> <eStructuralFeatures xsi:type=”ecore:EReference” name=”Tabla” upperBound=”-1” eType=”#//Tabla” containment=”true”/> <eStructuralFeatures xsi:type=”ecore:EAttribute” name=”Nombre” eType=”ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString”/> <eStructuralFeatures xsi:type=”ecore:EAttribute” name=”Esquema” eType=”ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString”/> </eClassifiers>

SOLO PROGRAMADORES nº 127

50

http://digital.revistasprofesionales.com


DISEÑO

Generación de código a partir de modelos con EMF

Volviendo al ejemplo, tras ejecutar la opción “Generate All” podemos comprobar que se han creado tres plug-ins diferentes: “model”, “edit” y “editor”. El plug-in “model”, creado dentro del proyecto original “EjemploEMF”, contiene el código que implementa el modelo independiente de plataforma (PIM) previamente definido con UML. Generalmente se crea una estructura de interfaces e implementaciones de los mismos, que no es necesario modificar salvo en ciertas ocasiones, como cuando hemos añadido Figura 4. Estructura de proyectos y de paquetes creada métodos a las clases de negocio después de pulsar en “Generate All”. en el modelo UML. Esto es debido a que no es posible especificar información EMF.edit”. Este plug-in separa la capa de presemántica en un modelo EMF hasta después sentación, representada por el plug-in “edide generado el código. tor”, del modelo de negocio, que ya vimos con Este plug-in de modelo hace referencia única- anterioridad, definiendo un conjunto de mente a otros dos plug-ins que forman parte adaptadores denominados ItemProvider para de la estructura básica de EMF y son “org.eclip- cada uno de los objetos de negocio. Estos se.core.runtime” y “org.eclipse.emf.ecore”. Es adaptadores nos permiten modificar el composible configurar determinadas opciones en portamiento de nuestros objetos en situacioel documento genmodel de modo que este nes tales como una notificación de cambio en modelo “EMF.model” pueda ser ejecutado el modelo. También podemos modificar los fuera del entorno Eclipse. Una de las principa- adaptadores para cambiar la representación les características de “EMF.model” es la inclu- gráfica de los objetos del modelo, así como de sión de características de notificación en cada sus atributos, en los diferentes componentes objeto del modelo de modo que se notifique a gráficos de Eclipse. Más adelante veremos un aquellos objetos registrados a los cambios en el ejemplo de esto último, cuando modifiquemodelo. En la figura 4 puede verse la estructu- mos la etiqueta y el icono de un determinado ra de proyectos y de paquetes creada. objeto. En el ejemplo podemos comprobar que dentro Por último, se genera el plug-in “editor” junto del directorio “src” del proyecto “EjemploEMF” con el proyecto “EjemploEMF.editor”, que prose han creado tres paquetes: porciona diferentes vistas y editores con los  e j e m p l o e m f: Contiene los interfaces que poder crear y modificar instancias de Java para cada uno de los objetos del nuestro modelo de clases. Junto con el editor modelo. Además se crean dos interfaces se crea un conjunto de clases por defecto, más: el primero que hereda de “EjemploemfEditor”, que construye las dife“EPackage” y se corresponde con el rentes páginas con los editores, paquete donde están incluidas las cla- “EjemploemfActionBarContributor”, que ses del modelo, y el segundo, heredero construye los diferentes menús contextuales de “EFactory”, donde se definen todos y pop-up que se mostrarán junto con los edilos métodos de creación de objetos de tores de nuestro modelo, “Ejemplo negocio. emfModelWizard”, que representa el wizard  e j e m p l o e m f . i m p l: Contiene la implede creación de las instancias del modelo de mentación de los objetos del modelo. Al negocio, y la clase de definición e inicializaigual que en el caso anterior, tendremos ción del plug-in, “BdEclipsePlugin”. una clase por cada clase del modelo así como las implementaciones de los Creando objetos en el editor interfaces Factory y Package.  e j e m p l o e m f . u t i l: Contiene dos clases Una vez visto las clases generadas por EMF, auxiliares. La clase “AdapterFactory”, vamos a ejecutar el editor que se ha creado. que proporciona adaptadores para cada Para ello accedemos a la opción “Run” -> “Run as” -> “Run-time Workbench”. Esto abrirá una objeto del modelo y la clase “Switch”. El plug-in “edit” se crea dentro de un nuevo nueva instancia del entorno Eclipse, pero esta proyecto de Eclipse denominado “Ejemplo vez con los plug-ins de nuestro ejemplo acce-

http://digital.revistasprofesionales.com

sibles. A continuación creamos un proyecto, para ello hacemos “File” -> “New” -> “Project” -> “Simple” y le damos un nombre, por ejemplo “ProyectoEMF”. Una vez que lo tenemos, nos creamos una nueva instancia a partir de nuestro modelo de negocio. Esto se hace ejecutando la opción de menú “File” -> “New” -> “Other...” y desplegando la carpeta “Example EMF Model Creation Wizards”,dentro de la cual seleccionamos “Ejemploemf Model” y pulsamos “Next”. De la lista de carpetas seleccionamos la del proyecto “ProyectoEMF” creado anteriormente y pulsamos “Next”. Ahora seleccionamos el objeto del modelo a partir del que queremos crear la nueva instancia del modelo, en nuestro caso va a ser “Proveedor”, y pulsamos en “Finish”. Ahora vamos a crearnos objetos y darle valores a los atributos. Para ello, en la vista “Selection” de nuestro editor, titulada con el literal “Resource Set” aparecerá el fichero “platform:/resource/ProyectoEMF/My.ejemploemf”. Esto representa la ruta al fichero donde se almacenará nuestro modelo en el entorno de trabajo. EMF utiliza la serialización como mecanismo de persistencia, almacenando los objetos del modelo en documentos con formato XML. Si abrimos el documento utilizando el editor generado por EMF, podremos observar que se ha creado un objeto de la clase “Proveedor” por defecto, junto con su atributo “Nombre”. Vamos a darle un valor al atributo para, a continuación crear más objetos dependientes del proveedor. Para ello debemos seleccionar el objeto “Proveedor” en el editor, esto mostrará en la vista de propiedades una tabla con el atributo “Nombre” sin valor. Para ver las propiedades del objeto “Proveedor” pulsaremos con el botón derecho del ratón sobre “Proveedor” y en el menú contextual elegiremos “Show Properties View”. Para dar un valor al atributo “Nombre” basta con pulsar en la columna “Value” y asignar el nombre del proveedor de la base de datos. Para añadir objetos al modelo, pinchamos sobre el objeto “Proveedor” y abrimos su menú contextual. Tenemos dos opciones (“New Chile”): crear un objeto “BaseDeDatos” o un objeto “TiposDeDatos”. Empezaremos creando un objeto “BaseDeDatos”. Si vemos sus propiedades, podemos comprobar que tiene dos atributos, “Esquema” y “Nombre”. Podemos asignarles los valores “SP” y “BDEjemplo”. Ahora vamos a crearnos un objeto “Tabla”. Pulsamos sobre el objeto “BaseDeDatos” y abrimos el menú contextual, seleccionando “New Chile” -> “Tabla”. En este caso, presenta dos atributos, “Descripción” y “Nombre”, a los que procedemos a asignar valores. Siguiendo el mismo procedimiento

51

SOLO PROGRAMADORES nº 127


DISEÑO

que hasta ahora, seleccionamos el objeto “Tabla” y le asignamos una “Columna”, “New Chile” -> “Columna”. Las propiedades de la “Columna” son “Descripción”, “Longitud”, “Nombre”, “Nulo”, “Clave” y “Tipo”. Es posible asignar valores a cada uno de ellos. Todas las propiedades de la columna tienen tipos simples por valor, salvo la propiedad “Tipo”, que almacena un valor de la clase “Tipo”, perteneciente a nuestro modelo de negocio. De hecho, si intentamos asignar un valor a la propiedad “Tipo”, se nos mostrará un combo con todos los tipos definidos en la instancia del modelo actual, es decir, se nos muestra una lista vacía. Esto es debido a que aún no hemos creado el conjunto de tipos permitidos para los campos de nuestro proveedor de bases de datos. Para ello, creamos un

Figura 5. Nuestro editor en funcionamiento.

LISTADO 2

Código del ejemplo en XML

<?xml version=”1.0” encoding=”UTF-8”?> <ejemploemf:Proveedor xmi:version=”2.0” xmlns:xmi=http://www.omg.org/XMI xmlns:ejemploemf=”http:///ejemploemf.ecore” Nombre=”BD Generica”> <BaseDeDatos Nombre=”BD Ejemplo” Esquema=”SoloP”> <Tabla Nombre=”Productos” Descripción=”Almacena la lista de productos”> <Columna Tipo=”//@TiposDeDatos/@Tipo.1” Nombre=”COD_PRO” Descripción=”Contiene el codigo del producto” Longitud=”3” Clave=”true”/> <Columna Tipo=”//@TiposDeDatos/@Tipo.0” Nombre=”Libro” Descripción=”Contiene el nombre del producto” Longitud=”15”/> </Tabla> </BaseDeDatos> <TiposDeDatos> <Tipo Nombre=”String”/> <Tipo Nombre=”Integer”/> </TiposDeDatos> </ejemploemf:Proveedor>

LISTADO 3

Código de la clase, generado automáticamente

/** * This returns the label text for the adapted class. * @generated */ public String getTextGen(Object object) { String label = ((BaseDeDatos)object).getNombre(); return label == null || label.length() == 0 ? getString(“_UI_BaseDeDatos_type”) : getString(“_UI_BaseDeDatos_type”) + “ “ + label; }

objeto “TipoDeDatos” por debajo del “Proveedor”, y le añadimos un conjunto de objetos “Tipo”, que podremos utilizar en la definición de las columnas. Un ejemplo para este editor podría ser el de la figura 5. También es posible editar el modelo utilizando el editor de texto, en cuyo caso tendremos acceso al documento XML que representa el modelo recién creado que, en caso del ejemplo, podría ser el mostrado en el listado 2. Ya hemos visto que es posible modificar el código generado en cualquiera de los módulos anteriores. Podremos introducir métodos en las clases del modelo, modificar el comportamiento de

SOLO PROGRAMADORES nº 127

52

los adaptadores y personalizar el editor, añadiendo nuevas acciones, etc.

Modificando el editor generado En este ejemplo vamos a realizar varias modificaciones sobre las clases generadas. El primer cambio consiste en modificar un adaptador, cambiando el nombre de una etiqueta y el icono asociado a ésta. Vamos a cambiar el nombre de la etiqueta para el objeto “BaseDeDatos”. Para ello nos vamos a la clase “BaseDeDatosItemProvider”, al método “getText(Object object)”.

Antes de empezar, debemos saber que si modificamos un método generado y regeneramos el modelo, perderemos los cambios. Para que no ocurra esto, debemos hacer lo siguiente: 

Hacemos una copia del método que queremos modificar, así siempre tendremos una copia del método generado.  Renombramos el original añadiéndole “Gen” al final, con lo que el nombre del método quedaría “getTextGen”. Esto indica que este método es generado automáticamente por el editor.  Al nuevo método creado le modificaremos el tag “@generated” por “@generated NOT”. Esto indica que este método no es generado automáticamente por el editor y si regeneramos todo el modelo no se sobrescribirá. Una vez hecho esto vamos a hacer los cambios sobre el método no generado, que consistirá en mostrar el valor del atributo “Nombre” del objeto “BaseDeDatos” en el editor. Véase esto en los listados 3 y 4. Hecho esto, ahora cambiaremos el icono. Para ello volvemos a la clase “Base

http://digital.revistasprofesionales.com


DISEÑO

Generación de código a partir de modelos con EMF

LISTADO 4

Código de la clase, ahora modificado

/** * This returns the label text for the adapted class. * @generated */ public String getTextGen(Object object) { String label = ((BaseDeDatos)object).getNombre(); return label == null || label.length() == 0 ? getString(“_UI_BaseDeDatos_type”) : getString(“_UI_BaseDeDatos_type”) + “ “ + label; } /** * This returns the label text for the adapted class. * @generated NOT */ public String getText(Object object) { String label = ((BaseDeDatos)object).getNombre(); return label == null || label.length() == 0 ? getString(“_UI_BaseDeDatos_type”) : label; }

LISTADO 5

Código del método, generado automáticamente

/** * This returns BaseDeDatos.gif. * @generated */ public Object getImage(Object object) { return getResourceLocator().getImage(“full/obj16/BaseDeDatos”); }

LISTADO 6

Código del método, ahora modificado

/** * This returns BaseDeDatos.gif. * @generated */ public Object getImageGen(Object object) { return getResourceLocator().getImage(“full/obj16/BaseDeDatos”); } /** * This returns BaseDeDatos.gif. * @generated NOT */ public Object getImage(Object object) { return getResourceLocator().getImage(“full/obj16/BaseDeDatos2”); }

DeDatosItemProvider”, al método getImage() (mostrado en el listado 5). Al igual que en el caso anterior, haremos una copia del método original. Una vez hecho esto vamos a hacer los cambios sobre el método no generado, que consistirá en cambiar el icono del atributo “Nombre” del objeto “BaseDeDatos” en el editor (véase ahora el listado 6). La imagen que queremos modificar debe estar almacenada en el directorio “eclipse/ workspace/EjemploEMF.edit/icons/full/obj16”. Si ejecutamos de nuevo el editor podemos ver estos cambios, tal como se refleja en la figura 6. El segundo cambio consiste en añadir una acción al menú pop-up asociado a un objeto del modelo mostrado en el editor. El primer paso es crearnos una clase que será la acción. En el plug-in “Ejemplo

http://digital.revistasprofesionales.com

EMF.editor” crearemos el paquete “ejemploemf/action” y la clase “GenerarDDL.java” dentro de él. Esta clase heredará de “org.eclipse.jface.action.Action”, al igual que cualquier acción que queramos añadir al editor (véase el listado 7). Lo único que va a contener esta clase es el constructor y la implementación del método “run()”, que es invocado por el entorno de ejecución de Eclipse cuando se selecciona la acción “Generar DDL” en el menú popup o contextual. Una vez creada la clase acción, debemos hacer unos cambios en el código generado por el editor para añadirla al menú pop-up que se muestra al pulsar el botón derecho del ratón con el cursor sobre alguno de los objetos del modelo. La clase que tenemos que modificar es “EjemploemfActionBarContributor.java”, que se encuentra en el paquete “presentation” del plug-in “EjemploEMF.editor”. Ya se comentó con anterioridad que esta clase es la encargada de dar forma a los diferentes menús a los que se tiene acceso al abrir algún editor generado por EMF. La primera modificación a esta clase consiste en importar la clase que representa nuestra nueva acción. Esta es la modificación: import ejemploemf.action.GenerarDDL;

El siguiente paso consiste en la creación de una variable de dicha clase “GenerarDDL”, que será la instancia de la acción que añadiremos a los diferentes menús. Este objeto será el responsable de ejecutar la acción cuando pulsemos la nueva opción creada en el menú pop-up de los objetos. A la definición de esta variable corresponde el siguiente código, también añadido a la clase: /**

Figura 6. Primer cambio sobre el código generado.

53

SOLO PROGRAMADORES nº 127


DISEÑO

* @generated NOT

LISTADO 7

Código de la clase GenerarDDL.java */

public class GenerarDDL extends Action { /** * The object select in the browser. If it is a Entry, the action is enabled */ private Object selection; public GenerarDDL(Object selection) { super(“Generar DDL”); this.selection = selection; } public void run() { Shell shell = Workbench.getInstance().getActiveWorkbenchWindow().getShell(); MessageDialog.openInformation(shell,”GenerarDDL”,”La generación se ha completado”); }

LISTADO 8

Código del método modificado

/** * This implements {@link org.eclipse.jface.viewers.ISelectionChangedListener}, * handling {@link org.eclipse.jface.viewers.SelectionChangedEvents} by querying *for the children and siblings * that can be added to the selected object and updating the menus accordingly. * @generated NOT */ public void selectionChanged(SelectionChangedEvent event) { ... ISelection selection = event.getSelection(); generarDDL = new GenerarDDL(selection); ... }

protected GenerarDDL generarDDL;

El siguiente cambio es efectuado en el método “selectionChanged”. Este método es invocado cada vez que se selecciona un objeto diferente en el editor de nuestro modelo, de modo que se crean todas las posibles acciones que aparecerían en el menú pop-up de ese objeto en particular. Esto es debido a que es posible personalizar los menús para cada objeto del modelo, mostrando unas acciones y ocultando otras en función de qué objeto aparece seleccionado en el momento de acceder al menú. Dentro de este método creamos una instancia de la acción añadiendo el siguiente código a la clase: generarDDL = new GenerarDDL(selection);

El código del método quedaría tal como muestra el listado 8. Para finalizar, modificaremos el método “menuAboutToShow”. Este método compone el menú pop-up a partir de un conjunto de botones y separadores. Para nuestro ejemplo añadiremos la siguiente línea de código: //Añadimos la acción al menú pop-up menuManager.insertBefore(“additions”,

LISTADO 9

Código del método modificado

/** * This populates the pop-up menu before it appears. * @generated NOT */ public void menuAboutToShow(IMenuManager menuManager) { ... //Añadimos la acción al menú pop-up menuManager.insertBefore(“additions”, generarDDL); }

generarDDL);

El código del método modificado quedaría como muestra el listado 9. Una vez realizados estos cambios, ya podemos ejecutar de nuevo el editor para comprobar el efecto de los mismos. Podemos ver en la figura 7 el resultado de ejecutar la nueva acción creada en el pop-up menú. Como se puede comprobar, es relativamente sencillo ampliar la funcionalidad por defecto de los editores generados por EMF añadiendo acciones aplicables tanto a un objeto en particular como a todo el conjunto de objetos del modelo en general.

Conclusiones EMF es una herramienta muy poderosa para la generación automática de código, ya que como se ha visto, a partir de un modelo permite generar las clases Java que implementan ese modelo. A esto, hay que añadir un uso intensivo de patrones de diseño en el código generado (Observer, Command, Adapter...) que garantizan que éste está bien codificado y sea fácil de entender. Figura 7. Creación de una acción.

SOLO PROGRAMADORES nº 127

54

http://digital.revistasprofesionales.com


ALGORITMOS

API Win32 y C: una programación directa y eficaz (y III) ADOLFO ALADRO GARCÍA

En esta tercera y última entrega desarrollaremos un ejemplo completo de aplicación, utilizando para ello nuevos controles y procedimientos del API Win32. Introducción La aplicación que se va a desarrollar como ejemplo que resuma esta serie de tres artículos consiste básicamente en una especie de “Administrador de tareas de Windows”. Mostrará una lista con las aplicaciones que se están ejecutando en ese momento en el sistema y además permitirá acceder a esas ventanas para activarlas y modificar su tamaño.

Los elementos de la interfaz La ventana de la aplicación consta de cuatro elementos: una lista donde se mostrarán todas las aplicaciones que se están ejecutando en ese momento en Windows, un botón para que la lista se cargue y, por último, un botón que hará que la aplicación seleccionada en la lista pase al frente modificando además el tamaño de su ventana a 800x600 píxeles. Todos estos componentes quedan reflejados en el fichero de recursos, “resources.rc”. El botón que

Fichero “resources.rc” que define los componentes de la interfaz.

SOLO PROGRAMADORES nº 127

56

obtiene la lista de las aplicaciones se identifica con el valor numérico 2, el botón que trae al frente la ventana cambiando su tamaño se identifica con el valor 3, y la lista se identifica con el valor 3. Estos identificadores son necesarios para poder acceder a los componentes después. Cuando la aplicación principal recibe el mensaje “WM_CREATE” se crea el correspondiente diálogo: hwndDialog = CreateDialog(GetModuleHandle(NULL), “DIALOG_0”, hwnd, DialogProc);

Donde la cadena “DIALOG_0” es el identificador del diálogo, también definido en el fichero de recursos. Posteriormente se ajusta el tamaño del diálogo y se muestra: GetWindowRect(hwnd, &rectHwnd); GetWindowRect(hwndDialog, &rectHwndDialog); MoveWindow(hwnd, rectHwnd.left, rectHwnd.top, rectHwndDialog.rightrectHwndDialog.left, rectHwndDialog.bottomrectHwndDialog.top, TRUE); ShowWindow(hwndDialog, SW_SHOW);

El procedimiento “DialogProc” es el responsable de procesar los eventos del diálogo. Se suele declarar en un fichero de cabecera o bien, como en este caso, al comienzo del código del propio fichero .c. Olvidar esto es un fallo muy común y cuando esto sucede la aplicación simplemente no compila porque cuando se hace referencia al procedimiento del diálogo, en algún punto del código antes de que el propio procedimiento haya sido declarado, el compilador no sabe resolver esa referencia. Cada vez que el usuario hace clic en alguno de los botones del diálogo el procedimiento “DialogProc” recibe el mensaje “WM_COMMAND”. La parte baja del parámetro “lParam” contiene en ese caso el identificador del componente que originó el evento, es decir, el identificador del botón. La macro “LOWORD” devuelve la parte baja de un valor dado. El esqueleto del procedimiento de diálogo queda por lo tanto como se muestra en el listado 1. Típicamente el procedimiento de diálogo http://digital.revistasprofesionales.com


ALGORITMOS

API Win32 y C: una programación directa y eficaz (y III)

LISTADO 1

Esqueleto de DialogProc

BOOL CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_COMMAND: { switch(LOWORD(wParam)) { case 2: { // Obtener ventanas y rellenar lista ... break; } case 3: { // Traer al frente la aplicación seleccionada ... break; } } break; } default: return FALSE; } return TRUE; }

LISTADO 2

Estructuras de datos para almacenar la información de las ventanas

typedef struct { HWND hwnd; char sWindowText[256]; int iIndex; } WindowInfo; typedef struct { WindowInfo windowInfoList[LIST_MAX_LENGTH]; int length; } WindowList;

devuelve “TRUE” cuando procesa el mensaje que recibe y “FALSE” en otro caso.

Enumerar ventanas El procedimiento “EnumWindows” enumera todas las ventanas correspondientes a las aplicaciones que en ese momento se están ejecutando en Windows. En el API Win32 queda definido tal y como sigue: BOOL EnumWindows(WNDENUMPROC lpEnumFunc, lParam);

Al igual que ocurre con otros muchos procedimientos del API, “EnumWindows” utiliza un mecanismo que se denomina en inglés “callback functions”. A grandes rasgos éste consiste en que el procedimiento, durante la enumeración, llama a otro procedimiento por cada uno de los elementos. El primer parámetro de “EnumWindows” es por lo tanto ese procedimiento, lo que se llama “callback function”. El segundo parámetro se http://digital.revistasprofesionales.com

emplea para hacer llegar algún parámetro al procedimiento que realmente procesa los elementos. En el caso que nos ocupa se empleará para pasar la dirección de memoria de una variable donde se irá almacenando la información relativa a las ventanas. Se han definido dos estructuras con el fin de almacenar la información relativa a las ventanas abiertas (véase el listado 2). Cada ventana quedará identificada por una estructura denominada “WindowInfo”. Esa estructura consta del manejador de la ventana (“hwmd”), la cadena de texto correspondiente al título y, finalmente, el índice que ocupa en la lista de ventanas, la cual está representada por la estructura “WindowList”. El atributo “windowInfoList” es un array de elementos “WindowInfo” que puede llegar a contener hasta “LIST_MAX_LENGTH” elementos. Este valor se define al comienzo del código mediante una sentencia define: #define LIST_MAX_LENGTH 256

Documentación del procedimiento “EnumWindows” de Microsoft Platform SDK.

El atributo “length” almacena la última posición ocupada del array anterior. Se podría haber optado por una solución mejor que consiste en no reservar inicialmente ningún espacio para ese array de datos, e ir solicitando más memoria únicamente cuando se necesite. Aunque evidentemente este método es mucho mejor desde un punto de vista de la optimización de los recursos, además de que permitiría guardar un número variable y no preestablecido de ventanas, su aplicación complicaría el código con llamadas a las funciones para reservar memoria, liberarla, etc. En primer lugar se declara una variable global de tipo “WindowList”: WindowList

windowList;

Esta variable se declara global, es decir, al comienzo del código, para que todos los procedimientos puedan acceder a ella en cualquier momento, ya sea durante el procesamiento de los clics de los botones, o durante la ejecución de “EnumWindows”. Cuando el usuario hace clic se inicializa la lista poniendo el atributo “length” a 0 (la primera posición libre del array) y se llama al procedimiento “EnumWindows”: windowList.length = 0; EnumWindows(EnumWindowsProc, (LPARAM)(&windowList));

El segundo parámetro es la dirección de memoria donde se encuentra la variable “windowList”. Ésta se obtiene empleando el operador ampersand (&). Como “Enum Windows” espera como segundo parámetro algo del tipo “LPARAM”, simplemente se hace una conversión de tipos, de forma que la dirección de memoria de la variable “windowList” pasa a ser un valor de tipo “LPARAM”. El procedimiento “EnumWindowsProc” se define tal y como sigue: 57

SOLO PROGRAMADORES nº 127


ALGORITMOS

BOOL CALLBACK EnumWindowsProc (HWND hwnd,LPARAM lParam) { WindowList* windowList; int

iCurrentIndex;

... }

La variable local “windowList” se emplea para poder acceder al parámetro “lParam” de una forma sencilla. La variable “iCurrentIndex” almacenará la propiedad “length” de la lista, que como ya se ha señalado es la longitud de la lista, o también la siguiente posición libre. El procedimiento “EnumWindows” normalmente suele devolver muchas más ventanas de las que se quieren. El propósito es obtener una lista lo más parecida posible a la que sale en el “Administrador de tareas de Windows”. Por lo tanto antes de procesar la ventana enumerada, añadiéndola a la lista, es preciso determinar si es “válida”: if (!IsWindowVisible(hwnd) || GetWindow(hwnd, GW_OWNER) != NULL) { return TRUE; }

El procedimiento “IsWindowVisible” determina si una ventana es visible o no, como su propio nombre indica. Todas aquellas ventanas que no sean visibles serán ignoradas. Por otro lado, el procedimiento “GetWindow” permite acceder a una ventana que tiene algún tipo de relación con otra ventana dada. El valor “GW_OWNER” se emplea para conocer el propietario de la ventana. Si el valor devuelto por “GetWindow” con este parámetro es distinto de “NULL” significa que la ventana enumerada es hija de alguna otra, en cuyo caso también se ignorará, ya que el propósito es

obtener la lista de las ventanas principales de las aplicaciones que en ese momento se están ejecutando en Windows, no la lista de todas las ventanas (todos los componentes). Durante la ejecución de “Enum WindowsProc” si el valor devuelto es “TRUE” entonces “EnumWindows” sigue con la numeración. En otro caso la enumeración se detiene. En el siguiente fragmento de código se determina el índice de inserción en la lista: windowList = (WindowList*)lParam; iCurrentIndex = windowList->length; if (iCurrentIndex>=LIST_MAX_LENGTH) { return FALSE;

windowList->length = windowList->length + 1;

El valor “lParam” es la dirección de memoria de la variable original “windowList”. En el contexto del procedimiento “Enum WindowsProc”, “windowList” es por lo tanto una variable de tipo “WindowList*”, o lo que es lo mismo, un puntero a una estructura. Por esta razón de aquí en adelante se accede a los elementos de la estructura empleando la flecha (->). Así “windowList->length” devuelve la longitud de la lista, o también la posición dentro del array donde se puede almacenar la información correspondiente a la ventana enumerada. Si el valor de ese indicador excede el valor máximo permitido, se devuelve “FALSE” y la enumeración concluye. Esto significaría que se han consumido todos los elementos disponibles del array, y éste ya no puede almacenar más referencias. Los datos se van almacenando en la lista simplemente empleando el índice determinado en el paso anterior:

windowList->windowInfoList [iCurrentIndex].iIndex = iCurrentIndex; GetWindowText(hwnd, windowList ->windowInfoList[iCurrentIndex]. sWindowText, 256);

SOLO PROGRAMADORES nº 127

58

Trabajar con listas

}

windowList->windowInfoList [iCurrentIndex[.hwnd = hwnd;

El procedimiento “SendMessageTimeout” se utiliza para mandar un mensaje a una ventana de forma que la llamada sea no bloqueante.

En este contexto “windowList” es un puntero a una variable de tipo “WindowList”, que a su vez es una estructura con dos miembros: “length” y “windowInfoList”. Ésta última es un array de datos y sus elementos pueden referenciarse simplemente usando los corchetes. Ahora bien, cada elemento del array es a su vez una estructura con los miembros: “hwnd”, “sWindowText” e “iIndex”. Es importante tener claro todas estas referencias o de lo contrario se producirán errores de compilación, que dentro de lo que cabe son los menos malos, o errores durante la ejecución de la aplicación por intentar acceder a una zona de memoria no esperada.

El procedimiento “GetWindowText” guarda el título de la ventana en el buffer de caracteres indicado en su segundo parámetro.

La lista es un control de tipo “ListBox” tal y como queda especificado en el fichero de recursos. Después de ejecutar el procedimiento “EnumWindows” la estructura almacenada en la variable global “windowList” guarda toda la información necesaria. El primer paso consiste en obtener una referencia a la lista empleando para ello el procedimiento “GetDlgItem”: hwndListBox = GetDlgItem(hwndDialog, 1);

El primer parámetro es el manejador correspondiente al diálogo y el segundo es un valor que identifica unívocamente al control. Después se recorre la estructura y se emplea el procedimiento “SendMessage” para ir rellenando las entradas de la lista: int i=0; for(i=0; i<windowList.length; i++) { SendMessage (hwndListBox, LB_INSERTSTRING, i, (LPARAM)windowList.windowInfoList[i]. sWindowText) ; }

El mensaje “LB_INSERTSTRING” indica que se desea introducir una cadena de texto como elemento de la lista. El valor “i” es el índice de inserción en la lista. Finalmente, el último parámetro es la cadena de texto en cuestión. Obsérvese que en este caso ya no se usa la flecha ya que “windowList” no es un puntero a una variable de tipo “WindowList” sino la variable misma. Si en el fichero de recursos se hubiera utilizado la palabra reservada “LBS_SORT” entonces la lista se ordenaría alfabéticamente de forma automática con lo que la inserción podría realizarse la siguiente forma: http://digital.revistasprofesionales.com


ALGORITMOS

API Win32 y C: una programación directa y eficaz (y III)

que esta llamada a “SetWindowPos” simplemente activará la ventana y cambiará el tamaño de la ventana, pero sin moverla de su posición original. En el API Win32 existen muchos procedimientos como “SetWindowsPos” que tienen un nombre que podría calificarse de poco afortunado, porque en realidad se emplean para otras muchas funciones, algunas de las cuáles ni siquiera tienen por qué estar relacionadas con lo que cabría deducirse por nombre. Normalmente estos procedimientos realizan una cosa u otra en función de un parámetro de flags, que contiene una combinación de palabras reservadas. Aplicación que muestra las ventanas activas de Windows. SendMessage (hwndListBox, LB_ADDSTRING, 0, (LPARAM)windowList.windowInfoList[i]. sWindowText) ;

con el del array de la estructura “WindowList”, acceder a la ventana correspondiente es tan sencillo como hacer: if (iSelIndex!=-1) { SetWindowPos(windowList.windowInfoList

Ahora bien, el problema que presenta esta solución es que entonces los índices de la lista no coincidirían con los índices del array “windowList.windowInfoList”, de forma que cuando se quisiera saber cuál es la ventana seleccionada no quedaría más remedio que ir comparando cadenas de caracteres. Se obtendría la cadena de texto correspondiente al índice seleccionado y después se buscaría en el array “windowList.windowInfoList” hasta dar con la misma cadena, y por lo tanto con el manejador de la ventana asociada. Por esta razón es preferible que los nombres de las ventanas aparezcan en la lista en la misma posición que ocupan en el array rellenado por el procedimiento “EnumWindows”, de forma que si el elemento seleccionado en la lista es por ejemplo el 3, en “windowList. windowInfoList[3]” se encontrará toda la información de esa aplicación. Cuando el usuario hace clic en el botón identificado con el valor numérico 3, entonces hay que traer al frente la aplicación correspondiente y redimensionar la ventana a 800x600 píxeles. Para obtener el elemento seleccionado de la lista se emplea de nuevo otro mensaje, “LB_GETCURSEL”: iSelIndex = SendMessage (hwndListBox, LB_GETCURSEL, 0, 0);

El valor devuelto, “iSelIndex”, almacena el índice de la lista seleccionado, o -1 en el caso de que no haya ningún elemento seleccionado. Como este índice coincide http://digital.revistasprofesionales.com

[iSelIndex].hwnd, HWND_TOP, 0, 0, 800, 600, SWP_NOMOVE); }

El procedimiento “SetWindowPos” tiene diversas funciones y todas dependen normalmente de la combinación de valores que tome el último de los parámetros. El primer parámetro es el manejador de la ventana correspondiente. El siguiente parámetro, en este caso la palabra reservada “HWND_TOP”, indica que se quiere traer la ventana al frente, por “encima” de todas las demás ventanas existentes en ese momento en Windows. Los dos siguiente parámetros hacen referencia a la posición de la ventana, pero como se verá más adelante simplemente se ignorarán. A continuación están los parámetros relativos a la anchura y la altura de la ventana. El último de los parámetros indica exactamente qué es lo que se quiere hacer con todos los anteriores. La palabra reservada SWP_NOMOVE señala que no se desea modificar la posición de la ventana, así

LISTADO 3

Acerca de la identificación de los recursos En aplicaciones complejas no es frecuente identificar los componentes de un diálogo por su número, ya que puede haber un gran número y su gestión puede complicarse. Por eso lo más sencillo es crear un fichero de cabecera en el que se definan dichos identificadores. Este fichero se incluirá tanto en el cuerpo del fichero de recursos, “resources.rc”, como en el cuerpo del código, “progwin32_03.c”. Para el primer caso se hará: #include “../progwin32_03.h”

La indirección se debe a que el fichero de recursos se encuentra en el directorio “resources”. Para “progwin32_03.c” la inclusión es: #include “progwin32_03.h”

El fichero de cabecera se crea en Dev-C++ seleccionando la opción “File” -> “New” -> “Source File”. El aspecto que presenta es muy simple: #ifndef _PROGWIN32_03_H_ #define _PROGWIN32_03_H_

Contenido del fichero resources.rc

#include <windows.h> #include “../progwin32_03.h” ICON_MAIN ICON “spp.ico” DIALOG_0 DIALOG 0, 0, 436, 204 STYLE DS_SETFONT |WS_CHILD |WS_VISIBLE FONT 8, “Tahoma” LANGUAGE LANG_NEUTRAL, 0 BEGIN CONTROL “Get list of windows”,BUTTON_GETAPPSLIST,... CONTROL “”,LISTBOX_APPLIST,”LISTBOX”,... CONTROL “800x600”,BUTTON_TOPANDRESIZE,”BUTTON”,... END

59

SOLO PROGRAMADORES nº 127


ALGORITMOS

#define LIST_MAX_LENGTH

256

#define BUTTON_GETAPPSLIST

2

#define BUTTON_TOPANDRESIZE 3 #define LISTBOX_APPLIST

1

ponentes. Así cualquier modificación en el fichero de recursos no origina que haya que modificar el código fuente principal, sino simplemente adaptar el fichero de cabecera.

#endif /* _PROGWIN32_03_H_ */

Iconos de las aplicaciones Las sentencias de preprocesador ifndef y endif se emplean para evitar que el fichero se incluya varias veces de forma accidental. Si la constante “_PROGWIN32_03_H_” (un nombre que se ha escogido al azar, no tiene que ser necesariamente ése) no está definida entonces se define, y el preprocesador comienza a analizar el cuerpo del fichero de cabecera. Si más tarde por algún motivo ese fichero intenta procesarse otra vez, la variable de preprocesador “PROGWIN32_03_H_” ya estará definida y por lo tanto se ignorará el cuerpo, saltando directamente al final de la sentencia endif. Habiendo declarado las constantes “BUTTON_GETAPPSLIST”, “BUTTON_TOPANDRESIZE” y “LISTBOX_APPLIST”, el fichero de recursos puede emplearlas a la hora de definir los componentes del cuadro de diálogo. Vea dicho fichero en el listado 3.

Esta simple aplicación podría crecer hasta convertirse en una especie de sustituto del “Administrador de tareas de Windows”, pero no estaría completa, al menos desde el punto de vista de la interfaz, si no mostráramos el icono correspondiente además del nombre de las ventanas. Cuando finaliza el procedimiento “Enum Windows” en la variable “windowList” se guardan todos los manejadores de las ventanas. Por consiguiente a partir de un manejador dado hay que poder obtener el icono asociado. Para ello se va a crear un nuevo procedimiento: HICON GetWindowIcon(HWND

hwnd);

Dependiendo del tipo de aplicación correspondiente a la ventana, los iconos pueden obtenerse de muy diversas formas. La primera de ellas, la más inmediata, consiste en mandar un mensaje “WM_GETICON” al manejador de la ventana: SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG|SMTO_BLOCK, 1000, (DWORD_PTR *)&hIcon);

El fichero de cabecera almacena las constantes que identifican a los componentes del diálogo.

Asimismo en el código del programa se accede a esos componentes utilizando las constantes: switch(LOWORD(wParam)) { case BUTTON_GETAPPSLIST: ... case BUTTON_TOPANDRESIZE: ... }

O bien: hwndListBox = GetDlgItem(hwndDialog, LISTBOX_APPLIST);

Esta es la forma más conveniente de trabajar con diálogos y por extensión con com-

SOLO PROGRAMADORES nº 127

60

El procedimiento “SendMessageTimeout” es igual que el procedimiento “SendMessage”, con la salvedad de que si la ventana receptora del mensaje no contesta pasado una determinada cantidad de tiempo, la función desiste, en otras palabras, no se queda bloqueada. En este caso es importante que se utilice “SendMessageTimeout” porque la aplicación a la que se manda el mensaje puede estar ocupada, o incluso colgada, y de otra manera sería nuestra propia aplicación la que terminaría colgándose esperando la respuesta al mensaje mandado. El parámetro “ICON_SMALL” hace referencia al tipo de icono que se está requiriendo. El valor “1000” es el número de milisegundos que estamos dispuestos a esperar. La palabra reservada “SMTO_ABORTIFHUNG” indica que la llamada se abortará si se detecta que la aplicación está colgada. Finalmente, “SMTO_BLOCK” señala que el procedimiento esperará hasta que se

obtenga la respuesta, o bien transcurra el tiempo estipulado. Si el valor de la variable “hIcon” es igual “NULL” después de ejecutar el procedimiento anterior, puede ocurrir que la aplicación no tenga ningún icono pequeño, con lo que se puede intentar pero requiriendo en este caso el icono grande: SendMessageTimeout(hwnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG|SMTO_BLOCK, 1000, (DWORD_PTR *)&hIcon);

Si no se obtiene éxito en ninguno de los dos casos anteriores, la siguiente opción consiste en obtener el icono de la propia clase de la ventana, para lo cual se emplea el procedimiento “GetClassLong”: hIcon = (HICON)GetClassLong(hwnd, GCL_HICONSM);

La palabra reservada “GCL_HICONSM” indica que se quiere obtener el icono pequeño asociado a la clase de la ventana. Del mismo modo que sucedía en el caso anterior, esta llamada puede fallar porque la aplicación no tenga un icono pequeño, con lo que también se puede intentar para el icono grande: hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);

Si todos los intentos anteriores fallan, entonces se obtiene el icono por defecto que Windows emplea para las aplicaciones: hIcon = LoadIcon(NULL, IDI_APPLICATION);

Conclusiones En esta serie se ha hecho una introducción a los aspectos básicos de la programación Win32 en C. Evidentemente no hay más que echar un vistazo la documentación disponible en msdn.microsoft.com para ser consciente de la cantidad enorme de temas que es imposible cubrir, bien por la extensión o bien por su complejidad. Aunque hoy en día pueda parecer que este tipo de desarrollos están obsoletos, lo cierto es que la mayor parte de las aplicaciones comerciales más conocidas utilizan este API de forma explícita, ya sea a través de un ejecutable o mediante librerías DLL, para acceder al “corazón” de Windows, de la forma más rápida, aunque también un poco más complicada. http://digital.revistasprofesionales.com


DUDAS

Preguntas y respuestas ADOLFO ALADRO GARCÍA

e.printStackTrace();

T e n g o u n c o n j u n t o d e f i c h e r o s , s a lvados por un servidor de correo Open Source en formato estándar, y m e g u s t a r í a s a b e r s i e s p o s i b l e t r at a r l o s c o n J a v a d e f o r m a a u t o m á t ic a , o s i p o r e l c o n t r a r i o l a d e c o d i f icación de los caracteres, y de todo el mensaje en sí, es algo que hay que programar desde cero.

} finally { try { if(bis!=null) bis.close();} catch (Exception e) {} }

El objeto “MimeMessage” recibe dos parámetros: el primero es la sesión y el segundo es un flujo de lectura de datos. La sesión es “null” porque en este caso tan sencillo sólo vamos a emplear el API de forma local, para leer mensajes almacenados en ficheros, es decir, la aplicación no se conectará a ningún servidor de correo para descargarse los mensajes. Una vez que el objeto de tipo “MimeMessage” está creado puedes acceder al asunto del mensaje, la fecha, etc.,

LISTADO 1 Página principal de Sun dedicada al API JavaMail.

El API JavaMail permite automatizar la mayor parte de las tareas de las que hablas. En realidad sus capacidades van mucho más allá, de forma que se puede implementar un servidor o un cliente de correo electrónico completo. Un fichero que almacena un mensaje de correo electrónico en un formato estándar presenta el aspecto del listado 1. El aspecto que muestra dicho listado puede variar mucho dependiendo de si hay ficheros adjuntos, del tipo de codificación empleada para las cadenas de caracteres, etc. Realmente existen muchas posibilidades. Sin embargo el API JavaMail es capaz de tratar esta información de forma casi transparente al programador. Así por ejemplo, el mensaje anterior podría leerse: BufferedInputStream bis = null; try { bis = new BufferedInputStream (new FileInputStream (new File(“msg.txt”)));

empleando para ello los métodos que proporciona. Por ejemplo: System.out.println(“mimeMessage.getSu bject()=” + mimeMessage.getSubject());

Quizás la parte más compleja es la del tratamiento de los ficheros adjuntos, ya que pueden existir varias posibilidades. Hay incluso casos en los que los mensajes, cuando tienen formato HTML, tratan esa información como una “especie de adjunto”. La rutina del listado 2 ofrece una manera de obtener el cuerpo de un mensaje, tanto si se trata de un mensaje simple como si es uno en formato HTML, con archivos adjuntos, etc. El “mimetype” determina el tipo de mensaje que es. Si el mensaje contiene archiMensaje de correo electrónico

From xxx@wanadoo.es Fri Apr 22 04:28:20 2005 Return-path: <xxx@wanadoo.es> Envelope-to: yyy@ya.com Delivery-date: Fri, 22 Apr 2005 04:28:20 +0200 Received: from [192.168.1.24] (helo=antivirus02.ya.com) by mx01.yacom.srv with esmtp id 1DOnu0-0002Ni-00 for yyy@ya.com; Fri, 22 Apr 2005 04:28:20 +0200 Received: from amavis by antivirus02.ya.com with scanned-ok id 1DOnu0-0004jm-00 for yyy@ya.com; Fri, 22 Apr 2005 04:28:20 +0200 Received: from [192.168.1.56] (helo=antimisp02) by antivirus02.ya.com with esmtp id 1DOntz-0002oN-00 for yyy@ya.com; Fri, 22 Apr 2005 04:28:19 +0200 Received: from mx.ya.com ([192.168.1.50]) by antimisp02 with ya id y2UK1R002G29f401 for yyy@ya.com; Fri, 22 Apr 2005 04:28:19 +0200 Received: from asmtp02.eresmas.com ([62.81.235.142]) by mx.ya.com with esmtp id 1DOntz-00078s-00 for yyy@ya.com; Fri, 22 Apr 2005 04:28:19 +0200 Received: from [192.168.108.73] (helo=mb03.in.mad.eresmas.com) by asmtp02.eresmas.com with esmtp (Exim 4.30) id 1DOntz-00034D-0w for yyy@ya.com; Fri, 22 Apr 2005 04:28:19 +0200 Received: from nobody by mb03.in.mad.eresmas.com with local (Exim 4.20) id 1DOnty-0008MA-NM for yyy@ya.com; Fri, 22 Apr 2005 04:28:18 +0200 From: “XXX” <xxx@wanadoo.es> To: adolfoaladro@ya.com Subject: hola, soy XXX, ¿hay alguien ahí?? Date: Fri, 22 Apr 2005 04:28:18 +0200 X-MAILER: ARB/3.0 Content-Type: text/plain; charset=iso-8859-1 Message-Id: <E1DOnty-0008MA-NM@mb03.in.mad.eresmas.com> X-Spam-Score: 3.5 (+++) X-Virus-Scanned: by YA.COM

MimeMessage mimeMessage = new MimeMessage((Session)null, bis)

Hola...

} catch (Exception e) {

SOLO PROGRAMADORES nº 127

62

http://digital.revistasprofesionales.com


DUDAS

Preguntas y respuestas

LISTADO 2

Obteniendo el cuerpo del mensaje

private final void showMsgBody(Part part) { try { if (part.isMimeType(“text/plain”)) { System.out.println(“Text plain message”); System.out.println((String)part.getContent()); } else if (part.isMimeType(“text/html”)) { System.out.println(“HTML text message”); System.out.println((String)part.getContent()); } else if (part.isMimeType(“multipart/*”)) { System.out.println(“Multipart message”); final Multipart multipart = (Multipart)part.getContent(); final int iMultipartCount = multipart.getCount(); for(int i=0; i<iMultipartCount; i++) { showMsgBody (multipart.getBodyPart(i)); } } else { System.out.println(“Other”); final Object object = part.getContent(); if (object instanceof String) { System.out.println((String)object); } } } catch (Exception e) { e.printStackTrace(); } }

vos adjuntos entonces el objeto “MimeMessage” es realidad un “MultiPart”, es decir, se compone se varios objetos “Part”, cada uno de los cuales representa un fragmento, o una parte del mensaje original. Entre esas partes se encuentra también el texto del propio mensaje. En un blog personal quiero ofrecer al usuario la posibilidad de elegir entre varios estilos a la hora de visualizarlo. Esto se traduce en tener que cambiar el CSS de toda la página dinámicamente, cuando el usuario selecciona uno de la l i s t a . ¿ D e q u é f o r m a p u e d o h a c e r-

lo para que funcione en la mayoría de los navegadores? En una página HTML pueden definirse varios elementos “link” que hagan referencia a hojas de estilo CSS, pero eso no significa necesariamente que todos se empleen. En el siguiente ejemplo se definen varios estilos, pero utilizando el atributo “disabled” se establece cuál es el que se aplica en ese momento: <link href=”blog1.css” rel=”stylesheet” type=”text/css” title=”blog1.css”/> <link href=”blog2.css” rel=”stylesheet” type=”text/css” title=”blog2.css” disabled/>

http://digital.revistasprofesionales.com

<link href=”blog4.css” rel=”stylesheet” type=”text/css” title=”blog4.css” disabled/> ...

El “truco” consiste ahora en decidir cuál de ellos está activado cuando el usuario realiza la sección. Véase para ello el listado 3. La función “SelectStylesheet” recibe como parámetro una cadena correspondiente al título del CSS (el atributo “title”) que el usuario ha elegido. En primer lugar se obtienen todos los elementos “link” de la página y se recorren hasta dar con aquel elemento que se corresponde con el estilo que en ese momento se aplica al documento. La propiedad “selectedStylesheet” del objeto “document” es la que determina la hoja de estilo actual. Cuando se encuentra, se modifica el valor del atributo “disabled” para que la hoja de estilo no se emplee. El segundo bucle realiza la operación contrario, estableciendo para el documento cuál es la hoja de estilo que debe aplicarse. En cuanto a la compatibilidad con los navegadores quizás la mejor forma de evitar problemas es simplemente comprobando la existencia de los objetos y de los métodos, de forma que si el navegador no lo permite, la función simplemente no hace nada. Por ejemplo: if (document.getElementById) { ... }

<link href=”blog3.css” rel=”stylesheet”

LISTADO 3

Ejemplo de un blog personal en el que se permite al usuario seleccionar la hoja de estilo con la que desea ver el blog.

type=”text/css” title=”blog3.css” disabled/>

Activando y desactivando hojas de estilo

function SelectStylesheet(styleSheet) { var oLinkElements = document.getElementsByTagName(“link”); var oLinkElementsLength = oLinkElements.length; for (var i=0; i<oLinkElementsLength; i++) { if (oLinkElements[i].title == document.selectedStylesheet) { oLinkElements[i].disabled = true; break; } } for (var i=0; i<oLinkElementsLength; i++) { if (oLinkElements[i].title == newStylesheet) { oLinkElements[i].disabled = false; document.selectedStylesheet = newStylesheet; break; } } }

63

SOLO PROGRAMADORES nº 127


CD-ROM

Contenido del CD-ROM Fuentes Creación de un sistema de distribución de Midlets (I) Nuestro objetivo, como el título de esta primera entrega indica, es extender las funcionalidades del servidor web IIS para que éste permita la descarga de aplicaciones para móviles, concretamente, Midlets. El código de la aplicación se encuentra empaquetado en el fichero “MidletServer_bin.zip” y para poder integrar y desplegar este código en IIS, será necesario seguir los pasos indicados en el fichero “readme.txt” o bien seguir las diapositivas que se incluyen en el directorio “TutorMidletServer”.

Novedades en los lenguajes de .NET 2.0 (y III) Los “generics” son una de las novedades más importantes que presenta la versión 2.0 de .NET y por lo tanto sus dos lenguajes principales, VB y C#, ofrecen sentencias para trabajar con ellos. El código que se adjunta en este punto debe servir como punto de partida para que el lector pueda practicar los “generics”

de modelos. EMF es, por lo tanto, una implementación de MDA para la plataforma Java que permite trabajar con la mayoría de las especificaciones englobadas dentro de MDA. En el CD-ROM, y bajo el título “Desarrollo de editores con Eclipse EMF” se encuentra el código de los ejemplos propuestos en nuestro artículo de la sección “DISEÑO”, esto es, “Generación de código a partir de modelos con EMF”. Al decir verdad, EMF permite tanto una cosa como la otra, por lo que animamos al lector a iniciarse en esta nueva filosofía de construcción de aplicaciones.

API Win32 y C: una programación directa y eficaz (y III) Con esta tercera entrega, llega a su fin una serie de tres artículos que nos han servido para explorar el corazón de Windows. Conocer el API Win32 permite hacer cosas realmente difíciles de concebir desde una programación a más alto nivel. Sin ir más lejos, en el proyecto que se ha incluido en el CD-ROM (importable desde Dev-C++) el lector comprobará que el API Win32 nos permite programar nuestro propio “Administrador de tareas de Windows”.

Las novedades incluidas son muchas, pero quizás la más llamativa es que incorpora un asistente para la “migración” de proyectos de Eclipse a netBeans, lo cual sin duda abre las puertas de este IDE a muchos desarrolladores Java.

nGallery 1.6.1

Programación

Aquellos programadores afines a las tecnologías ASP.NET y C# pueden sacar partido de nGallery de varias maneras. nGallery es una implementación de una galería de imágenes que puede ser incorporada a un desarrollo web ya existente, pero también puede servir para explorar su código y así seguir aprendiendo sobre el modelo de programación propuesto por ASP.NET, dado que nGallery es un producto Open Source.

Microsoft Developer Day

PHP Automatic Web Site 0.3

Por gentileza de Microsoft, se ha incluido en el CD-ROM el material expuesto en el Developer Day celebrado por la compañía. Este material consta de una serie de presentaciones, algunas acompañadas de ejemplos en forma de código, que reflejan las novedades más importantes del mundo .NET. Sin embargo, queremos advertir que este material fue diseñado para ser acompañados por una exposición oral, por lo que es posible que en algunas ocasiones el lector encuentre alguna dificultad para entender el código de forma íntegra.

PAWS nace con la idea de permitir a los usuarios generar sitios web dinámicos de forma fácil. PAWS es un CMS escrito en PHP que únicamente necesita para ser instalado el motor de bases de datos MySQL y el servidor web con soporte a la tecnología PHP.

Struts práctico (II) Llegamos a la segunda entrega de nuestro curso sobre la programación de aplicaciones J2EE con Struts, y esto nos conduce a entregar la segunda versión de nuestra aplicación SP.SHOP. En esta segunda versión, las novedades son importantes: internacionalización, Struts Tiles para las interfaces y validación de formularios con commons-validator. Para desplegar SP.SHOP será necesario seguir los pasos propuestos en el fichero “Leeme.txt”.

Creación de aplicaciones web con ColdFusion MX 7 (II) Como se ha venido comentando en las dos entregas de este curso, ColdFusion ofrece multitud de funciones que nos permiten hacer aplicaciones con muchas menos líneas de código. En lector encontrará en este punto una batería de ejemplos que demostrarán cómo ColdFusion puede trabajar con fechas, XML, controlar los errores, etc.

Generación de código a partir de modelos con EMF EMF es un framework Open Source para la construcción automática de código a partir

SOLO PROGRAMADORES nº 127

64

PNGwriter 0.5.3 PNGwriter es una librería que permitirá a los programadores C++ tratar con imágenes PNG. Existe numerosa documentación en castellano en la web del proyecto, aunque su uso es bastante sencillo.

SimpleSQL Como se ha dicho, EMF es una implementación de MDA para la plataforma Java. El artículo “Generación de código a partir de modelos con EMF” explica detalladamente cómo instalar y usar este plugin.

SimpleSQL es un motor de bases de datos relacionales ligero y sencillo que puede funcionar integrándose en una aplicación o por el contrario como servidor de bases de datos. SimpleSQL admite trabajar con C++, Java o PHP.

netBeans 4.1

Y además…

La versión 4.1 de netBeas ha visto la luz recientemente, y por este motivo hemos considerado apropiado el incluirla en el CD-ROM.

A modo de complemento para algunos de los artículos de este número, se han incluido también HSQLDB, Tomcat y Dev-C++.

Eclipse Modeling Framework 2.0.2

http://digital.revistasprofesionales.com


LIBROS

Gráficos y BBDD OpenGL AUTORES: Richard S. Wright, Benjamin Lipchak EDITORIAL: ANAYA ISBN: 84-415-1794-0 PÁGINAS: 1102 LUGAR Y AÑO DE PUBLICACIÓN: Madrid, 2005 IDIOMA: Castellano OBSERVACIONES: Incluye CD-ROM con el código fuente de los ejemplos y herramientas de desarrollo con OpenGL.

OpenGL es sin duda la primera API de gráficos 2D y 3D en tiempo real compatible entre plataformas. Su excelente estabilidad y su rendimiento se encuentran disponibles incluso en los PC más básicos de hoy en día. Se trata, además, de un estándar en los sistemas operativos UNIX, GNU/Linux y Mac, está incluso invadiendo los espacios móviles e incrustados, a través de una nueva especificación, OpenGL ES. Este completo manual proporciona todo lo necesario para programar con la nueva versión de OpenGL. Analiza el lenguaje Shading, las extensiones de sombreado ARB de bajo nivel, los detalles de programación para Windows, Mac OS X y GNU/Linux. Diseñado tanto para programadores que desean dominarlo y ampliar sus conocimientos sobre la programación de

gráficos 2D y 3D como para aquellos programadores experimentados que necesitan crear aplicaciones multiplataforma. Con este libro podrás… 

  

  

Descubrir los encabezados y bibliotecas que pueden usarse en OpenGL y configurar su entorno. Crear objetos tridimensionales en el PC. Mover tus objetos por un mundo virtual. Utilizar técnicas de interpretación rápida en tiempo real en Windows, Mac OS X y GNU/ Linux. Experimentar con la aceleración del hardware. Realizar escenas tridimensionales interactivas. Aprovechar el hardware de gráficos programable con el nuevo lenguaje Shading.

Oracle 10g: SQL, PL/SQL, SQL*Plus AUTOR: J. Gabillaud EDITORIAL: Ediciones ENI ISBN: 2-7460-2839-5 PÁGINAS: 496 LUGAR Y AÑO DE PUBLICACIÓN: Barcelona, 2005 IDIOMA: Castellano

Esta obra se dirige a todo informático que desee dominar la administración de una base de datos de Oracle. Repasa los conceptos, definiciones y reglas del modelo relacional y detalla su uso en el marco de las herramientas propuestas de modo estándar como Oracle Server 10g, es decir, SQL, SQL*Plus, PL/SQL y Java. Las técnicas de programación avanzadas en PL/SQL se estudian para poder utilizar toda la

SOLO PROGRAMADORES nº 127

66

potencia del servidor de bases de datos Oracle 10g y las novedades aportadas por esta versión: la interfaz iSQLPlus, la herramienta de diseño de aplicaciones web HTMLDB, la noción de flashback table y las expresiones regulares. Numerosos y precisos ejemplos ayudarán al lector a dominar estos lenguajes de referencia en el mundo de las bases de datos relacionales.

http://digital.revistasprofesionales.com

Revista Solo Programadores _127  

O Noticias, javaHispano y Opinión, Libros, Preguntas y Respuestas 8 413042303299 LA PRIMERA REVISTA DE PROGRAMACIÓN EN CASTELLANO ACTUALIDAD...

Read more
Read more
Similar to
Popular now
Just for you