Para ponernos en situación, imagina que tienes una aplicación en la que deseas enviar alguna notificación, por correo electrónico, a uno de los usuarios para informarle de alguna acción realizada por otro usuario.
MySQL.
Donde almacenamos el identificador del usuario que ha realizado la acción que da lugar a la notificación, el momento, el identificador del usuario al que debemos enviar el mensaje, el asunto, el texto del mensaje (en mi caso el texto esta formateado como HTML para que quede más bonito de cara al que lo recibe) y un booleano para saber si hemos enviado o no el mensaje.
Preparación del proyecto en Eclipse
En Eclipse (o cualquier otro IDE) creamos un proyecto Java, añadimos un paquete para nuestras clases y un directorio “lib” para albergar las librerías de JavaMail y del conector Java a MySQL.
Para poder utilizar estas librerías dentro de nuestro código pulsamos con el botón derecho del ratón sobre el proyecto, seleccionamos la opción “Build Path” y la subopción “Configure Build Path …”. Aquí vamos a la pestaña “Libraries” y agregamos los jars que hemos copiado anteriormente dentro del directorio “lib”.
Código Java
Con esto ya podemos empezar a escribir el código Java.
Aquí solo pondré algún trozo del código. Las clases completas las puedes coger de aquí:
http://bajoeltejadodezic.googlecode.com/svn/trunk/notificador/notificador.zip
http://bajoeltejadodezic.googlecode.com/svn/trunk/notificador/notificador.zip
La clase “Notificacion” carece de explicación. Define la entidad “Notificación” con sus atributos y nos permite que el código importante sea menos farragoso.
La parte importante del código la encontramos en la clase “Notificador”. Es una clase que se va a ejecutar con un programador de tareas que lanzará nuestro código a las 12:00 de la noche cuando se supone que todos los usuarios están “durmiendo” o al menos han dejado de trabajar con nuestra aplicación y ya todas las notificaciones a enviar están almacenadas en nuestra tabla. Además no queremos enviar 100 correos con 100 notificaciones a la misma persona por lo que leeremos la tabla y agruparemos las notificaciones en función del destinatario para sólo enviarle un correo con todas las notificaciones del día. Una vez que las notificaciones se han enviado marcaremos cada una de ellas en base de datos como enviadas para que no se vuelvan a enviar. Es muy importante saber que si el envío de correo se realiza dentro de una transacción jdbc si esta falla el correo no se enviara por lo que englobaremos el envío dentro de una transacción y así evitaremos reenviar las notificaciones si no somos capaces de marcarlas en nuestra base de datos como enviadas.
La ejecución del código empieza en el método “main” del notificador.
public static void main(String[] args) {
System.setProperty("mail.mime.charset", "ISO-8859-1");
Connection conexion = null;
PreparedStatement pstmt = null;
try {
String urldb = "jdbc:mysql://localhost:3306/nombreBaseDeDatos";;
String usuariodb = "usuarioDelEsquemaDeNotificaciones";
String clavedb = "claveDelUsuarioDelEsquemaDeNotificaciones";
Class.forName("com.mysql.jdbc.Driver");
conexion = DriverManager.getConnection(urldb, usuariodb, clavedb);
pstmt = conexion.prepareStatement("select idUsuarioRemitente, idUsuarioDestinatario, fecha, texto, asunto from notificacion where enviado=? order by idUsuarioDestinatario, fecha");
pstmt.setBoolean(1, false);
ResultSet rs = pstmt.executeQuery();
List<Notificacion> notificaciones = new ArrayList<Notificacion>();
Notificacion notificacion;
String idUsuarioDestinatario = null, idUsuarioDestinatarioAnterior = null;
while (rs.next()) {
notificacion = new Notificacion();
Clob clob = rs.getClob("texto");
notificacion.setTexto(clob.getSubString(1, (int)clob.length()));
notificacion.setAsunto(rs.getString("asunto"));
notificacion.setFecha(rs.getTimestamp("fecha"));
notificacion.setIdUsuarioRemitente(rs.getString("idUsuarioRemitente"));
idUsuarioDestinatario = rs.getString("idUsuarioDestinatario");
if (idUsuarioDestinatarioAnterior != null && !idUsuarioDestinatario.equals(idUsuarioDestinatarioAnterior)) {
enviarNotificaciones(getCorreo(idUsuarioDestinatarioAnterior, conexion), notificaciones, conexion);
notificaciones = new ArrayList<Notificacion>();
notificaciones.add(notificacion);
}
else
notificaciones.add(notificacion);
idUsuarioDestinatarioAnterior = idUsuarioDestinatario;
}
enviarNotificaciones(getCorreo(idUsuarioDestinatario, conexion), notificaciones, conexion);
}
catch(Exception e) {
// Código eliminado, ver el zip.
}
finally {
// Código eliminado, ver el zip.
}
}
Lo primero que hacemos es definir la propiedad para el conjunto de caracteres a utilizar por JavaMail con la línea:
System.setProperty("mail.mime.charset", "ISO-8859-1");
Esto no debería ser necesario pero sí es interesante comentarlo. Posiblemente, de no hacerlo, JavaMail utilice UTF-8 a la hora de codificar los caracteres pero, en mi caso, he tenido la mala idea de definir el campo “texto” de mi tabla, en MySQL, siguiendo la codificación por defecto de la tabla, y esta la tengo definida como “latin-1 default collation”. Si no pongo esta línea el resultado es que en mis mensajes los acentos, las eñes, y demás no aparecen correctamente.
A continuación crearemos los parámetros necesarios para crear la conexión con la base de datos, usuario, clave, url de la base de datos, cargaremos el driver jdbc de la misma y creamos un PreparedStatement para poder agruparlas por destinatario (cogiendo solo las no enviadas, enviado=0).
pstmt = conexion.prepareStatement("select idUsuarioRemitente, idUsuarioDestinatario, fecha, texto, asunto from notificacion where enviado=? order by idUsuarioDestinatario, fecha");
Recorremos el resultset y vamos almacenando en un ArrayList las notificaciones de un mismo usuario. En el momento en el que se cambia de usuario enviamos por correo las notificaciones a ese usuario y reiniciamos el ArrayList para albergar las del siguiente destinatario. En caso de error guardamos una traza para luego poder consultarla en un fichero. Posteriormente, al fin del proceso, cerramos la conexión.
En mi código el archivo de error va a “C:\\notificaciones” (la mala costumbre del Windows). Es curioso ver que Ubuntu me crea el log de este modo:
Dentro del método enviarNotificaciones definimos las propiedades necesarias para que JavaMail encuentre y utilice nuestra cuenta de Gmail (smtp.gmail.com, puerto 587) y para que envíe el correo, sacando la dirección de correo del destinatario de la tabla donde tenemos la dirección de ese usuario. Después de enviar la notificación las marcamos en base de datos como enviadas para no volver a tratarlas en el futuro.
public static void enviarNotificaciones(String correoDestinatario, List<Notificacion> notificaciones, Connection con) throws Exception {
if (correoDestinatario == null || notificaciones == null || notificaciones.size() == 0) return;
StringBuffer strBuff = new StringBuffer("<html><body>");
for (Notificacion notificacion: notificaciones) {
strBuff.append(notificacion.getTexto());
strBuff.append("<br/>");
strBuff.append("<br/>");
}
strBuff.append("</body></html>");
Properties props = new Properties();
props.setProperty("mail.smtp.host", "smtp.gmail.com");
props.setProperty("mail.smtp.starttls.enable", "true");
props.setProperty("mail.smtp.port", "587");
props.setProperty("mail.smtp.user", "cuentaDeGmail@gmail.com");
props.setProperty("mail.smtp.auth", "true");
Session session = Session.getDefaultInstance(props);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("cuentaDeGmail@gmail.com"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(correoDestinatario));
message.setSubject("Asunto de las notificaciones, p.e, Notificacion automáticas Sistema de ...");
message.setContent(strBuff.toString(), "text/html");
Transport t = session.getTransport("smtp");
t.connect("cuentaDeGmail@gmail.com", "claveDelUsuarioDeGmail");
t.sendMessage(message, message.getAllRecipients());
t.close();
actualizarNotificaciones(notificaciones, con);
}
Una vez que todo compila podremos arrancar nuestra base de datos y ejecutar el proyecto, como aplicación Java, directamente desde Eclipse para ver que todo funciona correctamente. Aunque desde Eclipse se puede hacer tengo la costumbre, posiblemente mala, de crear, por línea de comandos el jar de mis clases. Pulsando con el botón derecho del ratón sobre el proyecto podemos ver las propiedades del mismo y copiar la ruta del mismo. Vamos a un explorador, metemos la ruta, y copiamos el contenido del directorio “bin” en otra carpeta. Abrimos una línea de comandos, vamos a ese directorio y creamos el “jar” (librería para nuestras clases), notificador.jar:
Definición de la tarea en Ubuntu
Copiaremos los jars de conector MySQL, de JavaMail y de nuestro código en un directorio de Ubuntu.
La versión de Ubuntu sobre la que definí la tarea es la 10.04, utilizando el escritorio de GNome. Simplemente tenemos que ir a las herramientas del sistema y abrir las tareas programadas (no recuerdo si tuve que instalar algún paquete especial para esto, imagino que no, que es algo que viene por defecto en el sistema).
Crearemos una nueva tarea y definiremos sus propiedades, “Cada día” a las 00:00. El comando a ejecutar será del tipo:
java -classpath /home/david/notificador/notificador.jar:/home/david/notificador/mail.jar:/home/david/notificador/mysql-connector-java-5.1.12-bin.jar bajo.el.tejado.de.zinc.Notificador
En breve espero poner un ejemplo de uso: un formulario de sugerencias que notifica la entrada de alguna sugerencia al administrador del sistema.
No hay comentarios:
Publicar un comentario