Páginas

viernes, 25 de febrero de 2011

Implementación de un Comparator genérico en Java con ayuda de Groovy

JavaGroovy
Al programar en Java ¿no has tenido la necesidad de hacer unas cuantas clases que implementen la interfaz Comparator para realizar ordenaciones con el método Collections.sort pero no querías hacer una clase por cada una de ellas con unas pocas líneas? Cuando necesitas uno o dos Comparator puede valer hacer una clase para cada una de ellas pero cuanto empiezas a tener muchas la cosa empieza a exasperar por tener que hacer todo el rato lo mismo con un pequeño cambio, que es la expresión que obtiene los objetos que se comparan.

Para evitar tener que crear una clase por cada Comparator que queramos podemos hacer uso del soporte de Java 6 para los lenguajes de scripting (Consulta el enlace para ver como poder hacer uso de Groovy en Java) para crear un Comparator genérico. Básicamente la siguiente implementación de la interfaz Comparator, GroovyComparator, recibirá un script de groovy que lo aplicará a cada uno de los objetos del método compare de la interfaz Comparator y que devolverá un objeto que implememnte la interfaz Comparable por el que haremos la comparación entre los dos objetos.

package com.blogspot.elblogdepicodev.util;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class GroovyComparator implements Comparator {

 private String script;
 private Map bindings;

 public GroovyComparator(String script) {
  this(script, null);
 }

 public GroovyComparator(String script, Map bindings) {
  this.script = script;
  this.bindings = bindings;
 }

 @SuppressWarnings({ "unchecked" })
 @Override
 public int compare(T o1, T o2) {
  try {
   ScriptEngineManager manager = new ScriptEngineManager();
   engine = manager.getEngineByName(Constantes.GROOVY_ENGINE_NAME);
   
   Bindings b = engine.createBindings();
   if (bindings != null) {
    b.putAll(bindings);
   }

   b.put("it", o1);
   Comparable r1 = (Comparable) engine.eval(script, b);

   b.put("it", o2);
   Comparable r2 = (Comparable) engine.eval(script, b);

   return r1.compareTo(r2);
  } catch (ScriptException e) {
   throw new RuntimeException(e);
  }
 }
}

Suponiendo que tengamos una lista de objetos con una propiedad nombre (String) y queramos ordenar la lista por esa propiedad:

List l = ...;
 ...
 Collections.sort(l, new GroovyComparator("it.nombre"));

La verdad es que utilizando los lenguajes de scripting para hacer ciertas tareas se nos puede hacer la vida más fácil a los programadores.

Referencia:
Lenguajes de scripting sobre la plataforma Java
Scripting for the Java Platform
The Mustang Meets the Rhino: Scripting in Java 6

viernes, 18 de febrero de 2011

Enviar correos electrónicos mediante Java Mail

JavaTomcat
En algún caso puede que necesitemos enviar correos electrónicos desde una aplicación java. En esta entrada vamos a ver como realizarlo desde una aplicación web desplegada en un servidor tomcat y a través de una cuenta de correo de gmail.

Con estas premisas, sin más, el código java para enviar un correo elctrónico es el siguiente, que no sería muy distinto si utilizásemos otro servidor de aplicaciones e incluso no fuese una aplicación web:

...

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

...
try {
    // Obtener la sesión para enviar correos electrónicos del directorio JNDI
    Context ic = new InitialContext();
    Session session = (Session) ic.lookup("java:comp/env/mail/gmail");
   
    // Crear el mensaje a enviar
    MimeMessage mm = new MimeMessage(session);
  
    // Establecer las direcciones a las que será enviado 
    // el mensaje (test2@gmail.com y test3@gmail.com en copia oculta)
    mm.setFrom(new InternetAddress("test1@gmail.com"));
    mm.addRecipient(Message.RecipientType.TO, new InternetAddress("test2@gmail.com"));
    mm.addRecipient(Message.RecipientType.BCC, new InternetAddress("test3@gmail.com"));

    // Establecer el contenido del mensaje
    mm.setSubject("Hola mundo!");
    mm.setText("Hola mundo!");

    // Enviar el correo electrónico
    Transport.send(mm);
} catch (Exception e) {
    e.printStackTrace();
}

...

¿Pero donde se configura como nos conectamos al servidor de gmail para enviar los correos electrónicos? Pues bien, esto lo podemos hacer en la configuración de contexto para la aplicación. Estos  recursos se definen normalmente en un archivo «$TOMCAT_HOME/conf/Catalina/localhost/[contexto app].xml» y básicamente se asocia a un nombre del árbol JNDI un objeto de interfaz javax.mail.Session que será el que obtengamos en la aplicación y utilicemos para enviar los correos electrónicos. Esto es lo que hace la etiqueta Resource del ejemplo. JNDI es básicamente un registro donde se asocian nombres con servicios, en este caso el nombre «java:comp/env/mail/gmail» con objeto de interfaz javax.mail.Session que utilizaremos para enviar los correos electrónicos. La configuración para el tomcat sería:

<Context docBase="/home/tomcat/web">
    <Resource type="javax.sql.DataSource"
            auth="Container"
            maxActive="30" 
            maxIdle="10"
            maxWait="10000" 
            name="jdbc/db"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost/db?autoReconnect=true"
            username="root"
            password=""/>
    <Resource type="javax.mail.Session"
            auth="Container"
            name="mail/gmail"
            mail.transport.protocol="smtp"
            mail.smtp.host="smtp.googlemail.com"
            mail.smtp.port="465"
            mail.smtp.auth="true"
            mail.smtp.user="test1@gmail.com"
            password=""
            mail.smtp.starttls.enable="true"
            mail.smtp.socketFactory.port="465"
            mail.smtp.socketFactory.class="javax.net.ssl.SSLSocketFactory"
            mail.smtp.socketFactory.fallback="false"
            mail.smtp.debug="true"/>
</Context>

Al igual que podemos configurar un recurso de tipo javax.sql.DataSource en el mismo archivo de configuración de la aplicación  para obtener conexiones a bases de datos configuramos un recurso de tipo javax.mail.Session, lo que logicamente cambiará serán los parametros para configurar uno y otro. En ambos casos el parámetro name indica en que punto del árbol JNDI donde se deja disponible el recurso. En el ejemplo los parámetros indicados son los necesarios para conectarse al servidor smtp con una cuenta de gmail.

¿Y porque configuramos el DataSource y la Sesion en el tomcat? Podríamos evitarnos esta configuración y meterla en el código de la aplicación pero esto tiene la desventaja de que para utilizar una nueva configuración necesitaríamos recompilar la aplicación, en el caso de tener la configuración externalizada en un archivo «$TOMCAT_HOME/conf/Catalina/localhost/[contexto app].xml» únicamente sería necesario un redespliegue de la aplicación lo que es más rápido y sencillo.

Para hacer uso de las clases java del paquete javax.mail.* necesitaremos la librerias de java mail (http://www.oracle.com/technetwork/java/javamail/index.html) pero también necesitaremos las clases de JAF (JavaBeans Activation Framework) (http://www.oracle.com/technetwork/java/javase/downloads/index-135046.html). Ambas librerias activation.jar y mail.jar deberemos colocarlas en el directorio lib de tomcat (en la versión 7.x). El lugar donde ponerlas es importante ya que si las colocamos en el directorio WEB-INF/lib de la aplicación java mail no va a funcionar.

Desde luego este es un ejemplo básico de como enviar un mensaje de correo electrónico de un texto plano desde java y si tenemos necesidad de enviar correos electrónicos puede que también necesitemos enviarlos en formato html para lo cual quizá nos convenga hacer uso de motores de plantillas como freemarker o velocity.

Referencia:
http://es.wikipedia.org/wiki/JNDI
http://tomcat.apache.org/tomcat-7.0-doc/config/context.html
http://tomcat.apache.org/tomcat-7.0-doc/config/resources.html
http://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html

viernes, 11 de febrero de 2011

Unir Apache HTTPD y Tomcat mediante un reverse proxy

Apache HTTPDApache Tomcat

Al diseñar el despliegue de una aplicación es habitual hacer que un servidor web devuelva el contenido estático de la página web o de la aplicación. Esto nos puede interesar por varias razones, una de ellas es que quita la carga de trabajo correspondiente al servir el contenido estático al servidor de aplicaciones que dependiendo de la aplicación o página web puede ayudar a aliviar su carga. Pero también puede ser usado para realizar balanceo de carga entre varios servidores de de aplicaciones, cacheo o para acceder a un servidor de nuestra intranet que está detrás de un firewall y no es accesible directamente por los usuarios de internet. El esquema es el siguiente:


Viendo un gráfico se entiende mejor ¿verdad?. En él tenemos el cliente que realiza la petición, el servidor web que se encargará de devolver los recursos estáticos (en la figura denominado reverse proxy) y finalmente el servidor de aplicaciones que se encargará de generar el contenido dinámico de la aplicación (posiblemente accediendo a un servidor de base de datos que en el gráfico no se muestra, en la figura denominado Web1 y Web2). En un primer paso el cliente realiza una petición que llega al servidor web, este determina si el recurso solicitado lo puede servir él o lo tiene que delegar en el servidor de aplicaciones. En el caso de que el servidor web tenga que delegar la petición en el servidor de aplicaciones el servidor web realiza un petición al servidor de aplicaciones solicitando el recurso que no puede servir él. Una vez que el servidor de aplicaciones genera el recurso se lo devuelve al servidor web que a su vez este se lo devuelve al cliente. Para el cliente todo este proceso es transparente, es como si todos los recursos los pudiese servir el servidor web. En este esquema el servidor web actúa de proxy para el servidor de aplicaciones (tambien se llama reverse proxy).

¿Como se configura Apache HTTPD para actuar en modo reverse proxy?

En Apache para delegar las peticiones debemos utilizar las directivas ProxyPass, ProxyPassReverse, ProxyRequests y ProxyPreserveHost. En el primer parámetro de la directiva ProxyPass indicamos las URL que delegamos (/) y en el segundo parámetro indicamos la dirección del servidor de aplicaciones que devolverá realmente el contenido de la petición (ajp://localhost:8009/, ajp es un protocolo específico para comunicarse con Tomcat, también podríamos haber utilizado el protocolo http con http://localhost:8080/, que es lo que haremos si utilizamos otro servidor web que no sea Apache). Para que el servidor web devuelva los contenidos estáticos de la aplicación debemos indicarle a Apache que las peticiones que empiecen por /static/ no las delegue en el servidor de aplicaciones sino que las procese él, esto lo conseguimos con la directiva ProxyPass pero donde pondríamos la dirección del servidor de aplicaciones ponemos un !. En el siguiente ejemplo de configuración para Apache he definido un host virtual basado en nombres.

Descomentamos las siguientes líneas en el archivo httpd.conf:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_ajp_module modules/mod_proxy_http.so

# Virtual hosts
Include conf/extra/httpd-vhosts.conf

Lineas a añadir en el archivo httpd-vhosts.conf:

<virtualhost *:80>
 ServerName www.dominio.com
 ServerAdmin admin@dominio.com
 DocumentRoot "/apache/www"
 ErrorLog "logs/www.dominio-error.log"
 CustomLog "logs/www.dominio-access.log" common

 ProxyRequests Off
 ProxyPreserveHost On

 ProxyPass /static/ !
 ProxyPass / ajp://localhost:8009/
 ProxyPassReverse / ajp://localhost:8009/
 
 <directory />
  Order deny,allow 
  Allow from all 
 </directory>
 
 Alias /static/ "/apache/www/"
</virtualhost>

Ciertas aplicaciones del servidor de aplicaciones pueden necesitar conocer la máquina original que realizó la petición (hay que darse cuenta que para el servidor de aplicaciones todas las peticiones provienen del servidor web) esto se consigue con la directiva ProxyPreserverHost, de esta forma el servidor de aplicaciones podrá conocer cierta información del origen real de la petición.

Según esta configuración la aplicación en el Tomcat esta desplegada en el contexto ROOT y accederíamos a ella con http://www.midominio.com/. Este es un aspecto importante y que en pocos sitios se comenta, el contexto de la aplicación en Apache y en el servidor de aplicaciones ha de coincidir sino las URLs que devuelva el servidor de aplicaciones en el contenido no van a ser utilizables por el cliente. Más especificamente no va a funcionar lo siguiente:

ProxyPass / ajp://localhost:8009/aplicacion
ProxyPassReverse / ajp://localhost:8009/aplicacion

En todo caso deberiamos indicar /aplicacion como URL a delegar y desplegar la aplicación en Tomcat en el contexto /aplicacion:

ProxyPass /aplicacion ajp://localhost:8009/aplicacion
ProxyPassReverse /aplicacion ajp://localhost:8009/aplicacion

Si nuestro servidor web es Apache HTTPD y nuestro servidor de aplicaciones es Tomcat en vez de utilizar un reverse proxy también se puede utilizar el conector mod_jk para conectarlos pero he preferido utilizar esta forma de reverse proxy ya que el conector mod_jk no está disponible para otros servidores web y servidores de aplicaciones, es decir, el este concepto de reverse proxy es más universal y normalmente es implementado por la mayoría de servidores web lo que nos permite poder cambiar de servidor web fácilmente en caso de que quisiéramos. Podríamos cambiar más fácilmente Apache HTTPD por cherokee, lighttpd o nginx que son utilizados hoy día en importantes webs de internet.

Referencia:
Reverse proxy (wikipedia)

domingo, 6 de febrero de 2011

1º aniversario del blog

Pues pacere que fue ayer pero ya se ha cumplido un año desde que cree el blog. En todo este tiempo han sido 48 entradas las que he publicado hablando sobre Arch Linux y programación en java principalmente pero también he tenido tiempo para hablar sobre otros temas como Minix, personalización de Linux, juegos y algún otro. Durante este año he quedado bastante contento con las entradas que he publicado sobretodo con las referentes a Tapestry por haber podido devolverle algo por los buenos momentos que paso con él programando y del que espero seguir escribiendo.

En cuanto a los usuarios que visitan este blog prácticamente todos llegais por alguna búsqueda en Google, en estos momentos recibo en el blog unas 70 visitas diarias y durante este año en total han sido 24755 páginas vistas, 18200 visitas únicas con un promedio de 3 minutos en el sitio. Si ya sé, alguno dirá que no son muchas pero en el momento que inicie el blog no pensé que fueran tantas :).

Las entradas más visitadas han sido Ubuntu Enterprise Cloud, las guías de instalación de Arch Linux (I y II), como configurar Grub en modo gráfico y escuchar radios a través de internet con VLC.

A todos los que leáis esto, ¡gracias por visitar mi blog! espero que os resulte interesante.

viernes, 4 de febrero de 2011

Chrome Web Store

Ayer Google liberó otra versión estable de su navegador web, y ya van por la 9, en Arch Linux ya está en testing asi que no tardaremos mucho en tenerla disponible para actualizar.


Entre las principales novedades que incorpora esta versión es el soporte para WebGL que permitirá dotar a las aplicaciones de nuevas posibilidades. Si quieres probar que es esto de WebGL dale un vistazo a Chrome experiments. La otra novedad presentada junto con el navegador es la tienda de aplicaciones Chrome Web Store desde la cual prodremos instalar aplicaciones en el navegador Chrome.


En esta tienda podremos encontrar aplicaciones (Web Apps), extensiones y temas para el navegador Chrome. En cuanto a las aplicaciones están divididas en diversas categorias, educación, entretenimineto, juegos, noticias, etc... algunas son de pago, otras prueden probase y otras son gratuitas. Para adquirir las de pago deberemos hacer uso del sistema de pagos Google Checkout.


Entre las aplicaciones que podemos encontrar es el famoso juego Plants vs Zombies, otros buenos como The fancy pants adventure y algunas reediciones de clásicos como CodeBummer, Balloono y Bubble Witch todos estos gratuitos aunque algunos son una versión de prueba jugable.

Plants vs ZombiesThe fancy pants adventure

Code BummerBubble Witch

Sin duda otro gran paso para Google.