Archivos del sitio javahispano
Sun’s RI for JSF vs MyFaces
Época de despedidas, hoy le toca el turno a MyFaces
Después de más de dos años utilizando la implementación Apache Myfaces JSF, ha llegado el momento de las despedidas, en adelante todos nuestros proyectos se pasan a la implementación JSF 1.2 de Sun. No existe un motivo principal, pero si muchos pequeños problemas que finalmente nos han hecho decidirnos por la migración. Sin intención de entrar en detalle, estas son las motivaciones principales:
- En el momento de tomar la decisión, la implementación de Sun de la especificación 1.2 era mucho mas madura que la de Myfaces. En las pruebas realizadas pudimos comprobar que el numero de bugs es mucho mayor en la implementación Myfaces. En general Sun’s RI es una implementación mucho mas pulida.
- El número de incompatibilidades que hemos sufrido al migrar de la especificación 1.1 a 1.2 ha sido mucho menor con la implementación de Sun.
- Es posible seguir utilizando las librerías Tomahawk con la implementación de Sun.
- La implementación de Sun parece ofrecer mejor rendimiento que la implementación de Myfaces.
- La integración con Seam es mucho mas sencilla utilizando la implementación de Sun.
- Gracias a la filosofía una especificación, múltiples implementaciones; el cambio era posible.
Desarrollando un plugin Maven Report (I)
Hace varios meses me enfrenté al primer desarrollo de un plugin Maven para generar un report en el site, y comprobé en mis carnes que había muy muy poca documentación, y en castellano era totalmente inexistente. Por ello considero interesante describir paso a paso el proceso para desarrollar un plugin sencillo del tipo Hola Mundo!, dejando para un post futuro otros detalles interesantes, como la utilización de ficheros properties, introducción de parámetros de configuración, generación de ficheros externos al report, etc.
Este post supone que el lector ya está suficientemente familiarizado con la utilización de Maven, por lo que no entraré en detalles excesivamente básicos, como explicar lo que es un pom.xml, cómo se utiliza Maven, etc. Lo único básico que explicaré es que un plugin Maven Report sirve para generar un informe que se anexa al site del proyecto (se invoca, por ejemplo, ejecutando ‘mvn site’) siempre que esté referenciado en el pom.xml del proyecto llamante, dentro de <reporting><plugins>. El propio site de Maven ya embebe varios plugins Report como el de dependencias.
Con esta sencilla introducción, vamos al lío: lo primero, un plugin report es un proyecto Maven, por lo que tiene su pom.xml y su directorio /src/main/java. Veamos cómo sería nuestro pom.xml:
<?xml version="1.0" encoding="UTF-8"?><project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.viavansi</groupId>
<artifactId>report-plugin-demo</artifactId>
<packaging>maven-plugin</packaging>
<version>0.0.1</version>
<name>Plugin demo Report</name>
<inceptionYear>2008</inceptionYear>
<url>http://www.viavansi.com</url>
<organization>
<name>Servicios Avanzados para las Instituciones S.L. (VIAVANSI )</name>
<url>http://www.viavansi.com</url>
</organization>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.reporting</groupId>
<artifactId>maven-reporting-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.reporting</groupId>
<artifactId>maven-reporting-impl</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
</project>
He marcado en negrita los puntos clave: por un lado, debemos indicarle a Maven que este proyecto es un plugin mediante la etiqueta <packaging>, y además declaramos las dependencias a los APIs de construcción de plugins y reporting de Maven.
Una vez creado nuestro fichero pom.xml, estamos en disposición de crear la clase del plugin en el paquete que deseemos, obviamente dentro de /src/main/java.
Un plugin report debe extender de la clase org.apache.maven.reporting.AbstractMavenReport, lo cual obliga a sobrescribir los siguientes métodos:
- protected String getOutputDirectory(): devuelve el directorio donde se generará el fichero HTML del report
- protected MavenProject getProject(): devuelve una referencia objetual al pom.xml del proyecto padre que llama al report
- protected Renderer getSiteRenderer(): devuelve la instancia del objeto que rendea el site Maven
- public String getDescription(Locale locale): devuelve la descripción del plugin, permitiendo internacionalización en función del Locale.
- public String getName(Locale locale): devuelve el nombre del plugin, permitiendo internacionalización en función del Locale.
- public String getOutputName(): devuelve el nombre del fichero HTML que se va a generar.
- protected void executeReport(Locale locale): esta es la madre del cordero, es el método que se invoca para generar el informe.
Para implementar los 3 primeros métodos, es interesante saber que Maven entregará a nuestra clase 3 objetos:
- Una instancia de Renderer
- Una instancia de MavenProject
- Una instancia de File, que se corresponde con el directorio de salida por defecto (el /target/site)
De forma que los utilizaremos para implementar esos 3 métodos. Para que Maven nos entregue esas 3 instancias, deberemos declarar esos 3 atributos de la clase y utilizar Annotations. Veremos esto al incluir el código entero de la clase.
Otro punto clave es que es obligatorio incluir un goal Maven a cada plugin, con una anotación @goal.
Y un último concepto de importancia es cómo escribir el HTML. Para ello se hace uso del API Sink perteneciente al proyecto Doxia. Utilizando el método getSink() obtenemos el objeto Sink que nos permite escribir información al informe.
Veamos el código Java del plugin. He marcado en negrita algunas de las líneas clave, siguiendo las instrucciones que he comentado anteriormente. El código está comentado para explicar paso por paso lo que es está haciendo:
package com.viavansi.maven.report;
import java.io.File;
import java.util.Locale;
import org.apache.maven.doxia.siterenderer.Renderer;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.AbstractMavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.doxia.sink.Sink;
/**
* Genera un Hola Mundo en un site
* @goal demo
*/
public class DemoMojo extends AbstractMavenReport {
/**
* Directorio donde se generará este report
*
* @parameter expression="${project.reporting.outputDirectory}"
* @required
*/
private File outputDirectory;
/**
* Maven nos pasa este valor, no debemos configurarlo.
*
* @component
*/
private Renderer siteRenderer;
/**
* Instancia del proyecto (pom) que nos llamará.
* De aquí se puede sacar cualquier información del pom.
*
* Maven nos pasa este valor, no debemos configurarlo.
*
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;
@Override
protected String getOutputDirectory() {
return this.outputDirectory.getAbsolutePath();
}
@Override
protected MavenProject getProject() {
return this.project;
}
@Override
protected Renderer getSiteRenderer() {
return this.siteRenderer;
}
public String getDescription(Locale locale) {
return "Nuestro primer plugin Maven Report";
}
public String getName(Locale locale) {
return "DemoMavenReport";
}
public String getOutputName() {
return "demo";
}
@Override
protected void executeReport(Locale locale) throws MavenReportException {
//Ejemplo de log en consola
getLog().info("Iniciando plugin Demo");
//Hago las tareas que deba hacer, lo lógico sería delegar la responsabilidad
//Recupero el API Sink de Doxia para "pintar" resultados
Sink sink = (Sink)getSink();
//Cabecera del report
sink.head();
sink.title();
sink.text("Nuestro primer Report Maven");
sink.title_();
sink.head_(); //Se cierran las maquetaciones de forma similar a HTML
//Cuerpo del report
sink.body();
sink.section1();
sink.sectionTitle1();
sink.text( "Apartado 1" );
sink.sectionTitle1_();
sink.paragraph();
sink.text("Hola Mundo!");
sink.lineBreak();
//Ejemplo de utilización de información del pom del proyecto que llama
sink.text("Detectamos en el pom: "
+project.getGroupId()+"."+project.getArtifactId()+"-"+project.getVersion());
sink.paragraph_();
sink.section1_();
sink.body_();
}
}
Y listo. Para probar nuestro plugin, simplemente podríamos instalarlo en nuestro repositorio local (mvn install) o desplegarlo en algún repositorio de librerías, y referenciarlo desde otro proyecto Maven, en el apartado <reporting><plugins>…</plugins></reporting>. Veremos cómo se agregará nuestro report al site generado.
En un siguiente post mostraremos cómo podemos añadir características avanzadas a nuestros reports. Hasta entonces, feliz año a todos.
El falso Santo Grial de J2EE y la nueva esperanza JEE
Con visión retrospectiva podemos ver que muchas de las bases sobre las que se sustentaba J2EE fueron un error.
En los primeros días de J2EE, los xmls eran vistos como el Santo Grial de la configuración, el framework estaba pensado para que todo se pudiera y tuviese que configurar en un xml, incluyendo nombres de clases java y métodos. Esta técnica que sobre el papel parecía ideal para ayudar al desacoplamiento del sistemas tenía consecuencias nefastas en el desarrollo.
La configuración en ficheros xml resultó ser muy repetitiva, propensa a errores (muy difíciles de encontrar en tiempo de ejecución) y en definitiva demasiado compleja para el programador medio.
Ahora, mirando hacia atrás podemos ver que no solo el propio J2EE, sino también otros frameworks como Hibernate, Struts, Spring, iBatis, etc… cometieron el mismo error, y poco a poco la plataforma Java se ha ido haciendo cada vez más y más compleja, hasta ser considerada por algunos como un dinosaurio inmanejable, con una dura curva de aprendizaje.
Esta situación, conocida como “El infierno XML“, la han aprovechado muy bien otros lenguajes como Ruby on Rails con su lema programación por convención, que precisamente evita la trampa en la que cayó J2EE.
Por suerte la comunidad Java ha reconocido el problema del abuso de XMLs y ha actuado remplazando parcialmente los ficheros XML con una mezcla de anotaciones en el código y convenciones establecidas que sólo requieren la configuración de los casos excepcionales.
Como respuesta a estos y otros problemas, han empezado a aparecer algunas soluciones como por ejemplo:
* EJB3/JPA: Que simplifica dramáticamente el engorro que actualmente suponía trabajar con EJBs 2.0 o Hibernate y que desde hace un año forma parte de todas nuestras aplicaciones con unos resultados excelentes.
* Seam: Un framework ligero para Java EE 5.0 que simplifica el desarrollo de aplicaciones web y que despues de algunos proyectos internos, se convierte este año 2008 en nuestro framework base para todos nuestros nuevos proyectos web.
* Web Beans (JSR 299): La gran esperanza que parte del camino iniciado por Seam y que pretende convertirse, al igual que ya pasó con JPA, en el estándar para comunicar la capa web con los EJBs.
* Grails: Que ha resultado muy ilusionate en las pruebas y quizás en el futuro se convierta tambien en compañero de viaje.
* JAX-WS 2.x: Que simplifica enormemente tanto la publicación como el consumo de Servicios Web. Después de años utilizando Axis, JAX-WS pasa a convertirse en nuestra pila Web services preferida.
En definitiva, aunque los XMLs seguiran siendo parte esencial de nuestro día a día, un nuevo camino se abre en los paradigmas de programación Java, …
Serialicefa (Customiza tu Serializacion)
Segunda parte de Maldita Serializacion, la serie(lizacion) que causa sensa(liza)cion.
Como todos sabreis, no todos los objetos son serializables. Las instancias de Object, por ejemplo no son serializables. Y tampoco lo son los Thread, o los Socket.
Tambien sabreis todos que para Serializar un objeto que implemente una instancia de alguna clase no serializable, esta debe estar marcada como transient.
Lo que quizas no sea tan obvio es como sacar partido a lo explicado mi post anterior sobre serializacion. Como customizar nuestra serializacion, para que por ejemplo se rearranque un Thread al restaurar el objeto persistido.
El uso de writeObject y readObject como vimos anteriormente sobreescriben los comportamientos por defecto de la persistencia y restauracion de objetos por serializacion:
//Persistencia
//Dentro de try-catch
fos = new FileOutputStream(nombrefichero);
out = new ObjectOutputStream(fos);
out.writeObject(foo);
out.close();
//[...]
//Restuaracion
//Dentro de try-catch
fis = new FileInputStream(nombrefichero);
in = new ObjectInputStream(fis);
Foo foo = (Foo)in.readObject();
in.close();
Asi podemos hacer q nuestro foo al ser restaurado re-arranque determinado Thread:
private transient Thread t=new Thread(){
@Override
public void run() {
//Por ejemplo
MotorReglas.cache();
}
}
private void writeObject(ObjectOutputStream out) throws IOException{
//Escribe los campos no static ni transient de la clase actual en el stream.
//Debe ser llamado solo desde el metodo writeObject de la clase
//que esta siendo serializada.
//Lanza un NotActiveException si se llama desde otro lugar.
out.defaultWriteObject();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
//Lee los campos que no son static ni transient
//de la clase actual en el stream.
//Debe ser llamado unicamente desde el metodo readObject
//de la clase que esta siendo deserializada.
//Lanza un NotActiveException si se llama de otra forma.
in.defaultReadObject();
// Aqui restauramos las instancias transitient
t.start();
}
Y por ultimo, para que seais auntenticas criaturas del renacimiento:
http://www.cefatoys.com/web/estaticosvideos/video01.html
Hala pues,
Detectar a bajo nivel si un fichero es UTF-8 desde Java
Aunque inicialmente parece una tarea muy sencilla, los que hayan tenido que detectar si un fichero es UTF-8 se habrán dado cuenta que el tema no es ta obvio como parece.
Primero una introducción teórica
Los ficheros se almacenan como arrays de bytes que posteriormente son asociados a caracteres, para hacer esta asociación se utilizan diferentes codificaciones ( ASCII, ISO-8859-1,UTF-8,etc).
Para poder establecer una relación entre su código y cualquier carácter utilizado por cualquier lenguaje del mundo se creo Unicode, que no es mas que una gigantesca asociación código numérico-grafía para permitir su representación informática.
Dentro de este contexto, UTF-8 no es mas que una forma de codificar un texto Unicode para permitir su serialización en ficheros o flujos de datos.
Ya que Unicode intenta asociar códigos a todos los caracteres esenciales, necesitamos mas de un byte para codificarlos, por lo que UTF-8 recurre a una estructura variable de entre 1 a 4 bytes para codificar los diferentes caracteres.
Este tamaño variable es el motivo por el que en ocasiones los ficheros guardados con un formato se ven con caracteres extraños al recuperarlos utilizando la codificación incorrecta.
Aproximación al Algorítmo
El proceso es muy sencillo, basta leer el fichero byte a byte e ir comprobando que todos los bytes cumplen con lo especificado en el estandar UTF-8.
- Si el byte leído es menor que 0111 1111 (0×7F) es un byte válido. En este caso el byte representa un caracter UTF-8 (de 1 byte).
- Si el byte leído coincide con la mascara 110xxxxx, compruebo que el siguiente byte cumple con la mascara 10xxxxxx. En este caso los dos bytes leídos forman el carácter UTF-8.
- De forma similar se pueden detectar si son caracteres de 3 o 4 bytes.
Si en algún momento del procesamiento del fichero, alguna de las condiciones no se cumple, el fichero no es UTF-8, en otro caso tiene una codificación compatible con UTF-8.
Aunque seguro que existen implementaciones mucho mas eficientes en Java, después de algunas búsquedas en Internet no encontré nada, por lo que me lance a programar mi propio validador.
Recorte de código para detectar si un fichero está codificado en UTF-8
Configurando nuestro propio repositorio Maven
¿Para que queremos un repositorio propio?
- Para agilizar los tiempos de descarga, manteniendo una cache de las librerías utilizadas.
- Para reducir los conflictos entre librerías, controlando en todo momento las librerías disponibles.
- Mantener un repositorio central en el que localizar las librerías de la empresa.
- Un uso mas eficiente de ancho de banda.
¿ Que opciones tenemos?
Tenemos varias posibilidades, aunque la mas adecuada es crear un repositorio mixto, que por un lado almacene las librerías internas de la empresa, además de las librerías y drivers que no esten disponibles en los repositorios de públicos y por otro haga de proxy de las librerías de los repositorios centrales Maven.
De entres las diferentes alternativas:
- El estandar maven proxy (codehaus) y DSMP se descartan por ser demasiado simple, sin administrado, ni navegador visual.
- Artifactory de los que he probado es el mas completo, no es un mero proxy, sino que tiene funciones de backup y de búsqueda de librerías.
Proceso de instalación
La instalación es muy sencilla:
- Nos descargamos la última versión estable de http://www.jfrog.org/sites/artifactory/latest/ , actualmente la versión 1.2.1
- Descomprimimos el zip en el directorio base elegido, en adelante $ARTIFACTORY_HOME.
- Configuramos Artifactory, modificando el fichero $ARTIFACTORY_HOME/etc/artifactory.config.xml, dejando como están los repositorios y modificando en nuestro caso solamente el backup para que se realice todos los viernes
<backupCronExp>0 0 24 ? * FRI</backupCronExp>
- Desplegamos el $ARTIFACTORY_HOME/webapps/webapps/artifactory.war en el Tomcat 5+.
- Antes de iniciar el servidor es necesario declarar la variable de entorno artifactory.home, para que artifactory.war sepa donde localizar el directorio home.
en mi caso: export JAVA_OPTS=-Dartifactory.home=/home/felix/java/artifactory-1.2.1
- Arrancamos el servidor.
Configuración
- Accedemos a la aplicación http://localhost:8080/artifactory y accedemos al sistema por primera vez con el usuario “admin”, clave “password”.
- Lo primero que hacemos es configurar el usuario administrador, en el menú security/user seleccionamos el administrador y modificamos su password. También creamos un nuevo usuario deployer sin permisos de administración.
- Hacemos una carga inicial de todas las librerias que usamos y no se encuentran disponibles en ningun repositorio público. Tambien es posible importar un repositorio ya existente.
- Ya estamos listos para empezar a utilizar el nuevo repositorio, modificamos el pom de nuestros proyectos para que utilicen por defecto este repositorio.
<repositories>
<repository>
<id>Viavansi</id>
<name>Viavansi Repositorio</name>
<url>http://localhost:8080/artifactory/repo</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>Viavansi</id>
<url>http://localhost:8080/artifactory/repo</url>
</pluginRepository>
</pluginRepositories>
El resultado
Probamos a ejecutar cualquier comando sobre nuestro proyecto, por ejemplo mvn clean eclipse:eclipse y comprobamos que las dependencias son descargadas desde nuestro nuevo repositorio.
Downloading: http://localhost:8080/artifactory/repo/javax/activation/activation/1.1/activation-1.1.jar
61K downloaded
Tambien podemos probar a buscar una dependencia concreta, como por ejemplo de búsqueda, buscamos el artifac myfaces y vemos sus dependencias:
Y lo mas importante, tenemos todas las dependencias de nuestros proyectos perfectamente organizadas y localizables.
Algunos problemas
En nuestro caso, ya teníamos en Viavansi un repositorio interno Maven que gestionábamos manualmente y que mantenia desde hace casi un año cientos de librerias congeladas, esto nos ha causado muchos problemas ya que TODOS los proyectos que han empezado a utilizar el nuevo repositorio han requerido algún tipo de modificación, ya sea por cambios en las dependencias transitivas, por nuevas versiones de librerías, o por librerías que hemos tenido que volver a desplegar. En resumen, aunque la instalación de Artifactory es muy sencilla, dependiendo de cantidad de proyectos, la adaptación al nuevo repositorio puede ser compleja, en nuestro caso fueron dos duros días solucionando dependencias.
Mas información en la faq de Artifactory y articulo en Ingles en Theserverside.com.
Busca esto rápido
Encuentra lo que buscas de forma sencilla usando el buscador.
Categorías
Encuentra artículos a través de sus "tags"
Archivos mensuales
Encuentra artículos según el mes en el que fueron escritos.



