Ribe Software con pasión

Enero 17, 2008

Tecnologías para crear ‘Rich Internet Applications’

Hoy en día RIA, Rich Internet Application, es un término bien conocido. ¿Qué significa? El término RIA se refiere a aplicaciones web con una experiencia rica de usuario (como las aplicaciones de escritorio).

En el comienzo en la WWW todo era HTML (HyperText Markup Language) sin más. Teníamos una tecnología orientada al documento. Con HTML tienes texto, imágenes, tablas, formularios, y lo más importante: hiperenlaces. Cuando haces click en un hiperenlace eres dirigido a otra página web. En el navegador todo el contenido se reemplaza por el nuevo documento HTML: el contenido anterior desaparece. Además los formularios HTML son muy simples. ¿Qué haces si quieres que el usuario inserte una fecha en un campo del formulario? La única solución es dejar al usuario que envíe el formulario y entonces si el valor no es correcto redirigir al usuario de nuevo al formulario indicando los errores que debe corregir. ¿Y si el usuario quiere enviar un archivo muy grande? Sí, hay un componente HTML para enviar ficheros, pero ¿qué ocurre si el fichero es muy grande? El navegador no le dice al usuario cúanto tiempo tardará en enviarse o qué porcentaje se ha enviado ya. Simplemente dice “enviando petición” y eso es cualquier cosa menos algo usable. Y ¿qué ocurre si quieres que el usuario seleccione el típico “país” y “provincia” en un formulario? En una aplicación de eescritorio pondrías un combo para seleccionar el país y otro para seleccionar la provincia. Cuando el usuario selecciona el país el combo de provincias se rellena con las provincias de ese país obtenidas de una base de datos, por ejemplo. ¿Cómo puedes hacer eso sólo con HTML? Lo siento, no puedes. HTML no permite una experiencia rica de usuario.

Bien, para solucionar esta carencia se desarrolló JavaScript. Con JavaScript y el API DOM se puede cambiar cualquier cosa de una web de forma proramática. Muy útil. Puedes validar las entradas de un formulario en el cliente, puedes permitir al usuario seleccionar una fecha en un calendario,… Pero ¿qué ocurre cuando quieres obtener información del servidor como en el ejemplo de país-provincia? El primer truco era crear marcos internos ocultos (iframes). Con este truco puedes cargar datos en un marco separado y leerlos desde otro marco con JavaScript. Ese fue el comienzo de AJAX. Ahora tenemos XMLHttpRequest que es una manera mucho más elegante de hacer esto.

Demodo que ¡JavaScript permite construir Rich Internet Applications! Pero… ¿sabes? Hay gente a la que no le gusta JavaScript porque tiene muchas incompatibilidades entre navegadores, no soporta bien POO (programación orientada a objetos) y normalmente rompe la accesibilidad y la visibilidad (en los buscadores) de tus aplicaciones web.

Bueno, si no te gusta JavaScript hay algunas tecnologías no basadas en JavaScript para crear Rich Internet Applications:

  • Java applets. Son una tecnología que no está de moda pero durante algunos años era habitual crear applets para aplicaciones en intranets. Puedes comunicarte con el servidor a través de sockets. Probablemente tengas que firmar el applet digitalmente por razones de seguridad pero es no es un problema para aplicaciones destinadas a intranets. Y también necesitas que los usuarios tengan Java instalado pero, de nuevo, no es un problema para aplicaciones instaladas en intranets, pero sí puede serlo para aplicaciones destinadas a internet.
  • Adobe Flex. La idea es similar a los applets de Java: a través de un plugin en el navegador puedes incrustar pequeñas aplicaciones en cualquier documento HTML. En este caso se usan los lenguajes MXML y/o ActionScript para crear la interfaz de usuario y lógica de la aplicación. Entonces el código es compilado a una película Flash (.swf) que puede ser ejecutada en el Flash Player. Los beneficios son:
    • Flash está instalado en casi cualquier ordenador personal conectado a internet (todo el mundo ve vídeos en YouTube).
    • No hay problemas de compatibilidad porque sólo hay un Flash Player.
    • Facilidad de desarrollo, buena experiencia de usuario, interfaz gráfica agradable.
  • OpenLaszlo se basa en la misma idea que Flex pero con dos grandes diferencias:
    • Es OpenSource. Bueno, Flex lo será, pero por el momento no el Flash player.
    • Puedes compilar el código a Flash y también a HTML+JavaScript.
  • Microsoft Silverlight es una nueva technología que compite directamente con Adobe Flash y Flex.

¿Y si te gusta JavaScript pero no quieres reinventar la rueda o quieres usarlo lo menos posible? Puedes usar una solución JavaScript cliente o servidor.

Algunas soluciones del lado del cliente (librerías JavaScript):

  • Ext JS tiene un conjunto de componentes listos para usarse como tablas, árboles, diálogos,… Hablé de esta librería en un artículo anterior.
  • Dojo es similar a Ext JS pero en mi opinión es más limitado. Sin embargo tiene componentes muy útiles para crear mush-ups. Por ejemplo puedes integrar fácilmente Google Maps en tu aplicación.
  • Prototype es una librería JavaScript muy popular que simplifica el uso de AJAX en tus aplicaciones.
  • Otras: script.aculo.us que está basada en prototype y ofrece facilidades para crear animaciones, drag and drop, componentes AJAX, utilidades para DOM y pruebas de unidad; mootools también ofrece utilidades para AJAX y efectos con JavaScript.

Algunas soluciones del lado del servidor (frameworks del lado del servidor):

  • GWT (Google WebToolkit) es un framework Java. Con este framework puedes crear interfaces de manera muy similar a como harías con Swing y entonces, con un poco de magia, el código Java es compilado a JavaScript. En tiempo de desarrollo puedes probar tus aplicaciones en la máquina virtual de Java en un entorno simulado con los beneficios de que puedes usar cualquier herramienta de desarrollo disponible para Java. GWT hace el JavaScript transparente al programador.
  • JSF (Java Server Faces) es una tecnología Java que deriva de JSP (Java Server Pages). Se trata de escribir páginas JSP con los taglibs de JSF. Estos taglibs ‘dibujan’ comopnentes ricos. De modo que puedes escribir código Java que responde a los eventos de esos componentes (como en cualquier aplicación de escritorio). También hace el JavaScript transparente al programador. JSF está diseñado para permitir desarrollo rápido de aplicaciones (RAD, rapid application development). Hay bastantes herramientas para crear aplicaciones web basadas en JSF.
  • Ruby-on-Rails (RoR) es un framework pionero en ofrecer facilidades para aplicaciones basadas en AJAX.
  • Otros frameworks: hoy en día los frameworks más populares para cada lenguaje de programación traen facilidades para crear Rich Internet Applications. Algunos ejemplos: Symfony para PHP or TurboGears para Python.

Bien ¿hay alguna ‘experiencia rica de usuario’ más que queramos ver en nuestras aplicaciones web? Veamos. Todas las tecnologías anteriores se basan en la web (necesitan un navegador) y no puedes trabajar off-line con ellas. Si no estás conectado a internet no puedes usarlas. Para solverntar este inconveniente Google ha desarrollado una nueva tecnología: Google Gears. Con Google Gears puedes gestionar una base de datos relacional con JavaScript así que pudes almacenar grandes cantidades de datos en el cliente. De modo que puedes replicar datos en el lado del cliente permitando al usuario consultarlos cuando está desconectado. GMail soporta Google Gears así que si lo tienes intalado puedes leer tu correo aunque estés desconectado. Otros servicios de Google como Google Calendar también soportan esta tecnología.

¿Algo más? Vayamos más lejos. Ahora quieres mayor integración con el escritorio. Quieres que el usuario pueda abrir tu aplicación con doble click usando un icono personalizado y quieres que tus aplicaciones se ejecuten en una ventana independiente (no en el navegador), sin las típidas barras de herramientas: dirección, marcadores,… ¿Alguien ha pensado en esto antes?

  • Prism es un proyecto de la fundación Mozilla. Puedes ejecutar tus aplicaciones web como si fuesen aplicaciones de escritorio: con su ventana independiente sin barras de herramientas, con doble click, y de momento poco más.
  • Adobe AIR (llamado Apollo en la versión alfa) es un entorno de ejecución que permite usar muchas tecnologías como son: Flex, Flash, HTML, AJAX y PDF. Tiene un motor de renderizado web embebido (WebKit) para soportar todas estas tecnologías y ofrece algunas otras características:
    • Puedes generar un instalador para tu aplicación.
    • Icono que con doble click abre la aplicación en su propia ventana.
    • Puedes crear bases de datos relacionales locales porque viene con SQLite.
    • Soporte drag-and-drop con el sistema operativo.

Fantástico. Tenemos un gran conjunto de tecnologías. ¿Algo más? Bueno, sí. Hay otra forma de hacer Rich Internet Applications: puedes evitar cualquier lenguaje de programación basado en web y crear una aplicación de escritorio en cualquier lenguaje que obtenga los datos de web services. Por ejemplo: Google Earth, Flickr Uploader o Twitterrific. Con este método no tienes limitaciones en la experiencia de usuario. Pero pierdes una de las grandes ventajas de las aplicaciones web: la instalación y actualización en las aplicaciones web es transparente. Con Adobe AIR tienes el mismo problema. ¿Alguna solución? Desde hace unos cuantos años si desarrollas aplicaciones usando Java SE puedes distribuirlas con Java Web Start. Usando JWS (Java Web Start) defines un archivo XML en formato JNLP y con extensión .jnlp. En ese fichero defines dónde está alojada la aplicación, que versión del Java Runtime Environment se necesita y más cosas. Luego pones un hiperenlace en tu página web que enlace al fichero JNLP. Si el usuario tiene JWS asociado con esa extensión de archivo (puedes detectar con JavaScript si JWS está instalado) y hace click en el enlace Java Web Start descarga, instala y ejecuta tu aplicación. Cada vez que el usuario ejecuta la aplicación Java Web Start solicita al servidor si hay actualizaciones y las descarga si hay alguna. Por supuesto esta tecnología sólo está disponible para aplicaciones Java. Sin embargo si no usas Java o no quieres usar JWS siempre puedes programar tu propio sistema de actualizaciones a mano.

Para terminar también puedes crear un híbrido como se explica en otro artículo: Making double clickable Java EE web applications.

¿Algo más? ¡Sí! ¡No he hablado sobre tecnologías móviles! Quizá en un futuro artículo… De momento recuerda esto: las Rich Internet Applications han llegado y todas estas tecnologías de las que he hablado están cambiando la forma en cómo las aplicaciones web van a ser desarrolladas.

Enero 14, 2008

Java podría ser mejor

En los últimos días y semanas he leído artículos “contra” Java y estoy de acuerdo con ellos en la mayoría de sus argumentos. Creo que Java no es la mejor elección para todo y que Java no debería ser el único lenguaje enseñado en las universidades.

En este artículo voy a hablar de aquellas cosas en las que en mi opinión Java podría ser mejor. Creo que a veces Java es demasiado “verbose” (hay que escribir bastante código para tareas sencillas). Esto se debe a razones de sintaxis y también a métodos que en mi opinión faltan en la API estándar. Estos son algunos métodos que en mi opinión deberían estar incluídos en el API.

Collections

No hay una forma directa de añadir todos los valores de un array a una colección. Puedes añadir todos los elementos de una colección a otra con addAll(Collection<? extends T&gt ;) , pero no existe la manera de añadir todos los elementos de un array con un sólo método. No existe un método addAll(T[]) method. Se puede usar un truco para no iterar el array:

        List<String> list = new ArrayList<String>();         String[] array = { "foo", "bar" };         list.addAll(Arrays.asList(array));

Pero esto no es óptimo, por supuesto.

Otra cosa con la que he tenido que lidiar es porqué java.util.Properties no implementa Map<String, String> en vez de Map<Object, Object>. Por ejemplo, si tienes un Map<String, String> y quieres añadir todo el contenido de un objeto Properties no puedes usar el siguiente código:

        Map<String, String> map = new HashMap<String, String>();         map.putAll(System.getProperties());

Podrías usarlo si la clase Properties implementase Map<String, String>

Otra cuestión se cómo convertir colecciones en arrays. Puedes usar el método toArray(), pero tienes que pasar un array vacío como argumento.

        List<String> list = new ArrayList<String>();         list.add("foo");         list.add("bar");         String[] array = list.toArray(new String[0]);

Input / output

Leer de la entrada estándar (System.in) siempre ha sido una tarea tediosa en Java. No existe un método System.in.readLine() porque System.in es un InputStream. Tienes que crear un BufferedReader para leer líneas de la entrada estándar. Me encantaría tener algo como Console.ReadLine() (en C#). Bueno, en Java5 puedes usar un scanner. Pero… ¡hasta Java5 había que escribir mucho código! Este es un ejemplo de aplicación echo. implementada con la nueva clase java.util.Scanner:

        Scanner b = new Scanner(System.in);

        String line = null;

        System.out.println("echo examle, write 'quit' to exit");

        while(!"quit".equals(line)) {

            line = b.nextLine();

            System.out.println(line);

        }

        System.out.println("bye!");

No sé cuántas veces he tenido que escribir un InputStream entero a un OutputStream. Por ejemplo cuando necesitas copiar un fichero. Me encantaría que existiese un método writeTo() en la clase InputStream para poder hacer algo así:

new FileInputStream("foo.dat").writeTo(new FileOutputStream("bar.dat"));

Y también un método análogo para readers y writers. El método writeTo() estaría sobrecargado con una versión con dos argumetnos. Primero: la cantidad de bytes o caracteres que se desean leer y escribir (un número negativo significaría “todos”); y segundo: el tamaño de buffer que se usará. Este es un método de utilidad que uso en mis clases:

    public static void writeTo(InputStream in, OutputStream out, int buffsize, int length) throws IOException {

        byte[] buff = new byte[buffsize];

        int read = -1;

        do {

            read = in.read(buff, 0, length < 0 || buff.length < length ? buff.length : length);

            if(read == -1) break;

            out.write(buff, 0, read);

            length -= read;

        } while(true);

    }

Hay otras muchas tareas comunes a la hora de usar archivos que se tienen que implementar por uno mismo:

  • Copiar archivos.
  • Eliminar un directorio recursivamente.
  • Leer un archivo de texto a un String. Esto podría ser peligroso si el archivo es demasiado largo, así que el método podría tener un argumento para especificar el límite máximo de caracteres a leer.
  • Leer un InputStream o un Reader a un String.
  • Iterar las líneas de un fichero de texto más fácilmente. Por ejemplo en Python puedes hacer lo siguiente:
    for line in file("FileName.txt"):
    
      # Process line

Desde hace años uso CommonsIO para todas esas tareas, pero me gustaría tenerlas en el API estándar.
Integración de Swing con el escritorio

  • Quiero poder abrir un fichero con la aplicación instalada por defecto. Por ejemplo quiero que el usuario abra la ayuda de la aplicación en formato PDF con el lector de PDFs por defecto.
  • Quiero abrir una URL en el navegador por defecto. Por ejemplo quiero enviar al usuario a la página de pago en una aplicación shareware.
  • Quiero abrir el programa de correo por defecto Por ejemplo, quiero que el usuario pueda mandarme un email fácilmente desde la aplicación.

¡Por fín! Tenemos un API para eso en Java6.

Componentes Swing

  • ¡Quiero un componente para elegir una fecha en un calendario! ¡Esto es algo esencial en cualquier toolkit gráfico!
  • Por qué es tan complicado mostrar u ocultar todos los nodos en un JTree? Bien, puede ser peligroso dependiendo del TableModel porque los nodos en un TableModel se muestran bajo demanda. Un TableModel podría tener demasiados nodos (ej: todos los ficheros de un sistema de ficheros) o incluso podría tener infinitos nodos. Quizá sería útil tener un método en la interfaz TableModel: boolean canExpandAll(); y dos métodos en la clase JTree: expandAll() y collapseAll().

Algoritmos comunes

  • ¿Dónde está el soporte para codificar y decodificar en base64? Ah, lo encontré: import sun.misc.BASE64Encoder…. ¡Espera! ¡esto no es el API estándar! Si estás interesado en una implementación independiente del JRE usa este codificador / decodificador de base64 que es de dominio público.
  • Me gustaría poder hacer esto: Md5.hash(”foo”); o esto: SHA1.hash(”bar”); ¿Por qué no? ¡Y lo quiero en el API estándar!

Aplicaciones enterprise

  • Escribir XML. ¿Cuál es la forma estándar? Quiero un Document.write(…) o un Document.toString(). Vale, sé que esto depende del API W3C DOM y que existe un módulo “Load and Save” en DOM, pero no está incluído en el API estándar de Java. No me gusta W3C DOM por esta y otras razones. Si necesito una API estilo DOM prefiero JDOM.
  • Subir ficheros en una aplicación web. ¿Por qué el API de servlets es de tan bajo nivel? ¡Tengo que usar Commons FileUpload para poder leer un campo file de un formulario HTML!
  • Enviar correos electrónicos. Me encanta la función mail() de PHP. Me gustaría tener algo más sencillo que JavaMail en Java.
  • Para JDBC me gustaría tener métodos de utilidad como:
    connection.executeStatement("insert into users (first_name, last_name) values(?, ?)", "foo", "bar");
    
    connection.queryInt("select count(*) from users");

    Bueno, puedes hacer este tipo de cosas con Spring Jdbc DAO support y sus plantillas JDBC.

Propuestas de cambios de sintaxis

Además de los cambios en el API también voy a proponer algunos cambios en la sintaxis. Por ejemplo me gusta mucho la sintaxis para crear arrays en PHP. Por qué no algo así en Java:

Map map = new HashMap("first_name" => "foo", "last_name" => "bar");

O así:

List list = new ArrayList("foo", "bar");

Bueno, lo último sería posible si ArrayList tuviese un constructor con varargs.

Otra cosa que me gusta mucho es el method chaining (encadenamiento de métodos). Por ejemplo me gustaría poder hacer algo así:

        JFrame frame = new JFrame()

            .setTitle("title")

            .setIconImage(image)

            .pack()

            .setLocationRelativeTo(null)

            .setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)

            .setVisible(true);

En vez de devolver void devuelve this y podrás hacer encadenamiento de métodos. Esto no es un cambio en la sintaxis, es más bien una convención. Pero… el lenguaje podría devolver por defecto this en los métodos void no estáticos. Entonces podrías hacer encadenamiento de métodos sin implementarlo directamente.

Conclusiones

Me gusta el espíritu de Perl: “haz lo fácil, fácil, y lo difícil posible“. Y creo que en Java se podría hacer un mejor trabajo. Sin considerar los cambios en la sintaxis, ¿cuántas líneas de código se necesitarían para implementar los cambios en el API que he propuesto? Creo que no muchos. No quiero incluir todas las APIs de Apache Commons en la librería estándar. Ya hay demasiadas cosas. Pero creo que estas funcionalidades son muy útiles y fáciles de implementar. Demasiadas veces he tenido que echar un vistazo al Java Developers Almanac para encontrar la mejor forma o la más sencilla de implementar una tarea muy común. No obstante tengo que decir que Java6 ha introducido importantes cambios en el API. Y eso son buenas noticias.

Y sobre los cambios en la sintaxis se ha hecho un gran trabajo en Java 5: varargs, foreach, generics y el outboxing e inboxing. Estos cambios han hecho a Java menos “verbose“.

Resumiendo, Java es mejor en cada versión, pero podría ser aún mejor.

Blog de WordPress.com.