Archivo

Archivo para la categoría ‘Java’

Array de bytes y la clase java.nio.ByteBuffer

Jueves, 29 de Julio de 2010 jarrarte Sin comentarios

Un buffer es una secuencia finita de elementos -en este caso, tipos primitivos-. La clase abstracta java.nio.Buffer y sus respectivas subclases concretas (java.nio.ByteBuffer, java.nio.IntBuffer, java.nio.FloatBuffer, etc.) sirven como contenedores para datos de tipos primitivos. Estos buffers forman parte de la API NIO junto a las classes Channel, Selector, entre otras.

Estas clases, en particular java.nio.ByteBuffer, son de especial utilidad en aplicaciones en las cuales tenemos que generar array de bytes y que no justifica usar frameworks demasiado refinados para dicha tarea.

Un java.nio.Buffer tiene algunas propiedades escenciales:

  • Capacidad: la cantidad de elementos máxima del buffer. Siempre tiene un valor positivo, y no cambia durante la vida del objeto. Debe ser conocido al momento de inicializar el buffer.
  • Límite: según la documentación de java.nio.Buffer, el límite es “el índice del primer elemento que no debería ser leído ni escrito”. Es un valor entre la posición y la capacidad del buffer. La idea es utilizarlo como marcador para que, luego de terminada la escritura hacia el buffer, sepamos hasta donde lo podemos leer para obtener el array generado.
  • Posición: la posición actual dentro del bufffer. Es el índice del próximo elemento a leer o escribir.

Funcionalidades básicas

Todas estas funcionalidades están muy detalladas en los javadocs de las clases, pero es interesante repasar las más útiles:

  • Operaciones de escritura: la API provee las distintas variantes de operaciones put, permitiendo agregar bytes y tipos más grandes como ser int, long, float, arrays de bytes o incluso el contenido de otro ByteBuffer. Para los tipos de datos que ocupen más de un byte, se considerará la codificación (endianness) seteado utilizando el método order(). Tenemos métodos put para agregar bytes en posiciones relativas (en la posición actual) o absolutas (en una posición determinada)
  • Operaciones de lectura: al igual que con las operaciones put, las operaciones get permiten recuperar datos del buffer. Tenemos operaciones para recuperar diferentes tipos de datos (siempre teniendo en cuenta la codificación -endianness- cuando los tipos de datos son multi-byte), para recuperarlos desde la posición actual o desde una posición determinada. También podemos cargar un array de bytes con una sección del buffer.
  • clear(): prepara el buffer para una nueva secuencia de operaciones de escritura, seteando el límite con igual valor que la capacidad, y la posición actual en cero.
  • flip(): prepara el buffer para una secuencia de lecturas, seteando el límite igual a la posición actual, y la posición a cero. Más allá de la capacidad máxima del buffer, permite hacer operaciones sobre la porción que utilizamos anteriormente.
  • rewind(): prepara el buffer para una relectura de la información que contiene, seteando la posición en cero sin cambiar el valor del límite.
  • mark() / reset(): mark() setea la marca del buffer en la posición actual. al invocar reset(), cambiamos de posición en el buffer a la última invocación de mark().
  • slice(): crea un nuevo ByteBuffer a partir de la posición actual y hasta el fin del array. Los buffers comparten el array subyacente, por lo que un cambio en los datos se verá reflejado en ambos.
  • array(): retorna el buffer en forma de array.
  • Chaining (encadenamiento) de invocaciones: los métodos que no deben retornar valores al invocarlos, retornan una referencia a sí mismo, de manera de poder encadenar invocaciones al buffer. El encadenamiento no aporta nuevas funcionalidades, simplemente mejora la usabilidad.

Ejemplo de uso

Imaginemos que necesitamos generar un array de bytes para enviar a un sistema externo, que contiene la siguiente información:

Campo Tipo de dato Largo Formato
Largo del mensaje short 4 Little Endian
Id long 8 Big Endian
Largo del nombre short 4 Little Endian
Nombre String Máximo 30 -
Edad byte 1 -
Salario int 4 Big Endian

Debemos codificar el largo total del mensaje y el largo del nombre como little-endian (es decir, el byte menos significativo va en la primer posición, y el byte más significativo va en la última) en lugar de big-endian -a veces también llamado network order, por ser la codificación definida en el protocolo IP-.

Aprovecharemos las funcionalidades de la clase ByteBuffer para resolver este problema. En particular, ByteBuffer nos permite setear la representación de los enteros de varios bytes en el array, utilizando el método order(ByteOrder bo). Una solución que resuelve la generación del array podría ser la siguiente:

package com.josearrarte.demo;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ByteBufferDemo {

	private static final int SIZEOF_BYTE = 1;
	private static final int SIZEOF_SHORT = 2;
	private static final int SIZEOF_INT = 4;
	private static final int SIZEOF_LONG = 8;

	public static void main(String[] args) {

		byte[] byteArray = buildByteArray(12345678L, "Pedro Picapiedra", (byte)46, 25000);

		String hexString = getHexString(byteArray);
		System.out.println(hexString);
	}

	private static byte[] buildByteArray(long id, String name, byte age, int salary) {

		int capacity = 2 * SIZEOF_SHORT +	// largos
			SIZEOF_LONG + 					// id
			name.length() + 				// nombre
			SIZEOF_BYTE + 					// edad
			SIZEOF_INT;						// salario

		ByteBuffer buffer = ByteBuffer.allocate(capacity);

		// largo de datos
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		buffer.putShort((short)capacity);

		// id
		buffer.order(ByteOrder.BIG_ENDIAN);
		buffer.putLong(id);

		// largo del campo nombre
		buffer.order(ByteOrder.LITTLE_ENDIAN).putShort((short)name.length());

		// nombre
		buffer.order(ByteOrder.BIG_ENDIAN).put(name.getBytes());

		// edad
		buffer.put(age).putInt(salary);

		return buffer.array();

	}

	private static String getHexString(byte[] array) {
		final char[] chars = { '0', '1', '2', '3', '4',
				'5', '6', '7', '8', '9',
				'A', 'B', 'C', 'D', 'E', 'F' };

		StringBuilder strBuilder = new StringBuilder();

		for (int i = 0; i < array.length; i++) {
			byte b = array[i];

			byte lowNibble = (byte) (b & 0x0F);
			byte highNibble = (byte) ((b & 0xF0) >>> 4);

			strBuilder.append(chars[highNibble]);
			strBuilder.append(chars[lowNibble]);
			strBuilder.append(' ');
		}

		return strBuilder.toString();
	}
}

La salida del programa es:

21 00 00 00 00 00 00 BC 61 4E 10 00 50 65 64 72 6F 20 50 69 63 61 70 69 65 64 72 61 2E 00 00 61 A8

Podemos desglosar la salida para cada dato:

  • Largo de datos: 21 00 (33)
  • Id: 00 00 00 00 00 BC 61 4E (12345678)
  • Largo del campo de nombre: 10 00 (16)
  • Nombre: 50 65 64 72 6F 20 50 69 63 61 70 69 65 64 72 61 (“Pedro Picapiedra”)
  • Edad: 2E (46)
  • Salario: 00 00 61 A8 (25000)

Notemos la facilidad ByteBuffer que nos da a la hora de tener que intercambiar los formatos de enteros y de mezclar distintos tipos primitivos en un mismo array. No tuvimos necesidad de ir calculando las posiciones dentro del array, ni calcular la representación de enteros en formato little-endian o big-endian.

Referencias

API de java.nio.ByteBuffer
API de java.nio.Buffer
http://en.wikipedia.org/wiki/Endianness
http://en.wikipedia.org/wiki/New_I/O

Categories: Java Tags:

Cambiar la JVM utilizada por Eclipse

Jueves, 13 de Mayo de 2010 jarrarte Sin comentarios

En algunos casos es necesario iniciar el IDE Eclipse utilizando una JDK en lugar de la JRE por defecto. Un caso es, por ejemplo, para poder utilizar todas las funcionalidades del plugin m2eclipse. Veremos cómo cambiar la configuración para usar otra máquina virtual.

Para ello, debemos editar el archivo eclipse.ini que está en el directorio de instalación de Eclipse (bah, en el directorio en donde lo descomprimimos), y agregar el parámetro -vm indicando el ejecutable de la JDK que usará el IDE. Hay algunas cosas a tener en cuenta:

  1. El parámetro -vm debe ir solo en una línea, con su valor en la línea siguiente.
  2. El valor del parámetro -vm es la ruta hasta el ejecutable (por ejemplo javaw.exe), no es la ruta del directorio que lo contiene.
  3. Es importante la ubicación del parámetro -vm. Debe estar ubicado antes del parámetro -vmargs. Todos los parámetros luego de -vmargs serán pasados directamente a la JVM

Yo tengo una JDK en Windows instalada en el directorio D:\Java\jdk1.6.0_17, y éste es mi eclipse.ini:

-startup
plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.0.200.v20090519
-product
org.eclipse.epp.package.jee.product
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
-vm
D:/Java/jdk1.6.0_17/bin/javaw.exe
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx512m

Como último punto a destacar, quisiera comentar que toda esta configuración se puede verificar en About Eclipse, en el menú de Help.

http://wiki.eclipse.org/Eclipse.ini
http://tech.karolzielinski.com/m2eclipse-eclipse-is-running-in-a-jre-but-a-jdk-is-required

Categories: Eclipse, Java, Maven Tags: , ,

Generación del manifest de un JAR utilizando Maven

Viernes, 30 de Abril de 2010 jarrarte 3 comentarios

Al empaquetar un artefacto Java, puede interesarnos agregar información al manifest generado. El manifest es un archivo que se crea dentro de nuestro JAR (en la ruta META-INF/MANIFEST.MF) con información bastante diversa acerca del JAR, en forma de "propiedad: valor". Esta información puede ser acerca de la versión de código empaquetada, del classpath, de la clase que se ejecutará al correr java -jar [archivo.jar] o inclusive propiedades personalizadas que tienen significado para el usuario de ese JAR.

Utilizando Maven podemos generar automáticamente el archivo MANIFEST.MF. Para ello, debemos editar el archivo pom.xml agregando la configuración del manifest al plugin que se encarga de empaquetar nuestro proyecto (maven-jar-plugin). Veamos un ejemplo:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.josearrarte</groupId>
	<artifactId>tests</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<url>http://www.josearrarte.com/blog</url>

	<dependencies>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>jar</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<archive>
						<manifest>
							<classpathPrefix>lib</classpathPrefix>
							<addClasspath>true</addClasspath>
							<mainClass>com.josearrarte.demo.MainClass</mainClass>
						</manifest>
						<manifestEntries>
			              <sample_entry>sample_value</sample_entry>
			              <url>${project.url}</url>
			            </manifestEntries>
					</archive>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Lo más importante del ejemplo está en la parte de configuración del maven-jar-plugin. Ahí tenemos tres elementos configurados:

  • mainClass: Se utiliza para determinar qué clase contiene el método main del JAR cuando ejecutamos la JVM con el parámetro -jar [archivo.jar]
  • addClasspath: los valores posibles son true o false. Si el valor es true se agregará una entrada Class-Path al manifest, con la lista de JARs de los cuales depende nuestro artefacto (algunos scopes no serán considerados, por ejemplo test)
  • classpathPrefix: este elemento de configuración es útil cuando queremos referenciar los JARs de los que depende nuestro artefacto y están en un directorio diferente. Se pueden utilizar paths relativos

Los elementos dentro de sirven para agregar propiedades adicionales al manifest. Se pueden utilizar variables del estilo de ${project.build.finalName} que serán sustituidas sus valores reales. No conozco todas las variables existentes, pero aquí hay una lista que parece bastante completa.

El archivo META-INF/MANIFEST.MF resultante de empaquetar el proyecto será muy similar al siguiente:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: jarrarte
Build-Jdk: 1.6.0_19
Main-Class: com.josearrarte.demo.MainClass
sample_entry: sample_value
url: http://www.josearrarte.com/blog
Class-Path: lib/commons-dbcp-1.4.jar lib/commons-pool-1.5.4.jar

En el pom.xml del proyecto de ejemplo tenemos una dependencia a commons-dbcp, que a su vez depende de commons-pool. Esto se ve reflejado al construir el valor classpath en el manifest, que contiene las dos dependencias.

Referencias

http://tutorialexception.blogspot.com/2009/03/configurar-el-manifest-con-maven-para.html
http://maven.apache.org/
http://maven.apache.org/shared/maven-archiver/examples/classpath.html
http://maven.apache.org/guides/mini/guide-manifest.html
http://docs.codehaus.org/display/MAVENUSER/MavenPropertiesGuide
http://java.sun.com/docs/books/tutorial/deployment/jar/modman.html

Categories: Java, Maven Tags: ,

m2eclipse: un plugin de Eclipse para proyectos de Maven

Sábado, 17 de Abril de 2010 jarrarte Sin comentarios

m2eclipse es un plugin de Eclipse para la integración con Maven. Permite usarlo para manejar, proyectos simples, multi-módulo, ejecutar builds de Maven utilizando la interfaz de Eclipse e interactuar con repositorios de Maven. El plugin ya está bastante maduro, y resulta muy cómodo para trabajar con este tipo de proyectos.

La instalación es bastante simple. Tenemos que agregar el update site, que es http://m2eclipse.sonatype.org/sites/m2e. Hay una única opción para instalar, llamada “Maven Integration for Eclipse (Required)“. Se instalará el core, el editor de POMs y la integración al repositorio de Maven.

En el sitio oficial hay links a videos bastante descriptivos acerca del proceso de instalación, creación de proyectos, etc.

m2eclipse trae un Maven embebido. Si ya tenemos una instalación de Maven configurada y un repositorio local con artefactos descargados podemos aprovecharla, configurando m2eclipse para que haga uso de ella. Para agregar instalaciones de Maven a m2clipse, debemos acceder al menú de preferencias de Eclipse, agregar la instalación seleccionando el directorio en el cual está ubicado y marcarla como la instalación activa:
Configurar instalaciones de maven

El plugin cuenta con bastante documentación. Es posible descargar la referencia de m2eclipse en PDF desde éste link (es necesario llenar algunos datos) o leer la documentación on-line aquí. Tiene una FAQ bastante completa, y el blog de m2eclipse es éste.

La página principal del plug-in es http://m2eclipse.sonatype.org/index.html

Categories: Eclipse, Java, Maven Tags: , ,

Tests parametrizados con TestNG

Jueves, 15 de Abril de 2010 jarrarte Sin comentarios

En el post anterior vimos cómo podíamos implementar tests parametrizados utilizando JUnit 4. TestNG es un framework similar a JUnit, compartiendo entre ellos casi la totalidad de sus features.

TestNG propone un enfoque un poco diferente a la implementación de tests parametrizados. Me pareció más flexible y prolija, por lo que creo que vale la pena continuar el post anterior con el ejemplo implementado utilizando este framework.

En TestNG, los pasos que debemos seguir para implementar un test parametrizado son los siguientes:

  • Escribir un método anotado con @DataProvider(name = “[nombre]“), donde [nombre] es el nombre por el cual se referenciará a este DataProvider en los métodos de test. Si los juegos de datos tienen más de una variable, deberá retornar un array multidimensional. Si es necesario -generalmente lo es-, podría contener además el resultado esperado para cada juego de datos de entrada.
  • Escribir un método anotado con @Test(dataProvider = “[nombre]“), que reciba tantos parámetros como cantidad de datos se tenga en cada item del array que retorna el método anotado con @DataProvider.

No hay necesidad de aislar los tests parametrizados de los tests que no lo son. Se puede inclusive tener varios DataProviders en una misma clase, asociando métodos de tests diferentes a cada uno.

Veamos un ejemplo de la case de test utilizando TestNG (el proyecto está disponible para bajar aquí):

package com.josearrarte;

import static org.junit.Assert.assertEquals;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class ParametrizedTestNGTest {

	@DataProvider(name="test1")
	public Object[][] createData1() {
		return new Object[][] {
				{ DateFormatProvider.OPERATION_TYPE_1, "MMYY" },
				{ DateFormatProvider.OPERATION_TYPE_2, "YYMM" },
				{ DateFormatProvider.OPERATION_TYPE_3, "YYYYMM" } };
	}

	@Test(dataProvider="test1")
	public void verifyDateFormats(int operationType, String dateFormat)
		throws Exception {
		DateFormatProvider provider = new DateFormatProvider();

		String result = provider.getDateFormatFor(operationType);
		assertEquals(dateFormat, result);
	}

	@Test(expectedExceptions=IllegalArgumentException.class)
	public void verifyDateFormats_ShouldThrowIllegalArgumentException()
		throws Exception {
		DateFormatProvider provider = new DateFormatProvider();

		provider.getDateFormatFor(-1);
	}
}

Como vemos, la idea es exactamente la misma que en JUnit, pero está mejor implementada. Los tests parametrizados se diferencian mejor, y no es necesario tener miembros de la clase y constructores comunes para almacenar los datos.

Además, el hecho de separar la creación de los tests (instanciación) con la generación de los juegos de datos (data providers) nos permite tener en una misma clase tests con y sin parámetros.

Categories: Java, Testing Tags: ,

Tests parametrizados con JUnit 4

Jueves, 15 de Abril de 2010 jarrarte Sin comentarios

Una de las nuevas características de JUnit 4 es la posibilidad de correr tests parametrizados. Los tests parametrizados son una forma de escribir un test genérico y poder correrlo con juegos de datos diferentes.

Creando tests parametrizados

Para crear un test parametrizado con JUnit, necesitamos seguir los siguientes pasos:

  • Crear una clase de test anotada con @RunWith(Parameterized.class). El nombre completo del runner es org.junit.runners.Parameterized.
  • Agregar miembros de la clase que representen los parámetros de los tests.  Estos miembros no necesitan tener getters y setters, y pueden ser miembros privados.
  • Crear un constructor común de la clase, que reciba parámetros del tipo de los miembros y les asigne sus valores.
  • Crear un método estático que retorne una java.util.Collection.  Este método deberá estar anotado con @Parameters, y será el que define el juego de datos a utilizar.  Si los juegos de datos tienen más de una variable, deberá retornar una colección de arrays. Si es necesario -generalmente lo es-, podría contener además el resultado esperado para cada juego de datos de entrada.
  • Escribir un método anotado con @Test, en el cual se usen los miembros de la clase antes definidos.

Ejecución de tests parametrizados

La ejecución de un test parametrizado se realiza de forma idéntica a un test normal.

Al ejecutar el test, JUnit invocará al método anotado con @Parameters, construirá la clase de test tantas veces como juegos de datos haya, invocando cada vez al constructor con un juego diferente de datos. En cada una de esas instancias del tests ejecutará el test de forma normal, invocando -si existen- a los métodos anotados con @BeforeClass, @AfterClass, @Before, @After y al test en si. En resumen, la ejecución será:

  • @Parameters
  • @BeforeClass
  • @Before
  • @Test (juego de datos 1)
  • @After
  • @Before
  • @Test (juego de datos 2)
  • @After
  • @Before
  • @Test (juego de datos N)
  • @After
  • @AfterClass

Ejemplo

A modo de ejemplo imaginemos que tenemos una clase com.josearrarte.DateFormatProvider con un método getDateFormatFor(int operationType) y que, dependiendo del valor del operationType recibido, retorna un java.lang.Stringcon diferentes formatos de fecha (“MMYY”, “YYMM” o “MMYYYY”).

El código completo del ejemplo está disponible para bajar aquí.

package com.josearrarte;

public class DateFormatProvider {

	public static final int OPERATION_TYPE_1 = 1;

	public static final int OPERATION_TYPE_2 = 2;

	public static final int OPERATION_TYPE_3 = 3;

	public String getDateFormatFor(int operationType)
		throws IllegalArgumentException {

		switch (operationType) {
			case OPERATION_TYPE_1: {
				return "MMYY";
			}
			case OPERATION_TYPE_2: {
				return "YYMM";
			}
			case OPERATION_TYPE_3: {
				return "YYYYMM";
			}
			default: {
				throw new IllegalArgumentException("Unknown operation type");
			}
		}
	}
}

Podríamos generar una serie de tests parametrizados para los flujos básicos de ejecución y otro test para le caso que operationType no sea 1, 2 o 3. La clase de test podría ser como la siguiente;

package com.josearrarte;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class ParametrizedJunitTest {

	private int operationType;
	private String dateFormat;

	public ParametrizedJunitTest(int operationType, String dateFormat) {
		super();
		this.operationType = operationType;
		this.dateFormat = dateFormat;
	}

	@Parameters
	public static Collection operationTypeValues() {

		return Arrays.asList(new Object[][] {
				{ DateFormatProvider.OPERATION_TYPE_1, "MMYY" },
				{ DateFormatProvider.OPERATION_TYPE_2, "YYMM" },
				{ DateFormatProvider.OPERATION_TYPE_3, "YYYYMM" }
		});
	}

	@Test
	public void verifyDateFormats() throws Exception {
		DateFormatProvider provider = new DateFormatProvider();

		String result = provider.getDateFormatFor(operationType);
		assertEquals(dateFormat, result);
	}

	@Test(expected=IllegalArgumentException.class)
	public void verifyDateFormats_ShouldThrowIllegalArgumentException()
		throws Exception {

		DateFormatProvider provider = new DateFormatProvider();

		provider.getDateFormatFor(-1);
	}
}

Dejo planteado un problema que no supe resolver con JUnit. Si escribo un test parametrizado y otro que no es parametrizado en una misma clase, JUnit correrá a ambos como si fueran parametrizados, tantas veces como el tamaño de la colección de los datos. No encontré forma de configurar JUnit para que uno de los métodos (verifyDateFormats_ShouldThrowIllegalArgumentException() en el ejemplo) se ejecute como un test común y corriente. En éste post muestro cómo se puede resolver este tema utilizando TestNG en lugar de JUnit 4.

En resumen

Los tests parametrizados nos simplifican la tarea de escribir varios tests cuando su implementación es similar entre ellas. Generalmente aporta claridad al código de la clase de test, ya que no se escribe tanto código repetido.

Sin embargo, si utilizamos JUnit debemos escribir los tests parametrizados en una clase separada de los tests comunes, ya que no es posible decirle a JUnit la forma de ejecutar un método de test y otro. Si no queremos tener este problema podemos utilizar TestNG, otro framework de testing unitario. En éste post muestro detalles de cómo hacerlo.

Categories: Java, Testing Tags: ,

“Unmappable character for encoding UTF-8″ al compilar proyectos Java con Maven

Viernes, 20 de Noviembre de 2009 jarrarte 1 comentario

Si al compilar un proyecto no da errores del tipo: /ruta/al/proyecto/src/main/java/paquete/Clase.java:[48,30] unmappable character for encoding UTF8 y en esa línea tenemos caracteres con tilde, ñ u otros caracteres “problemáticos”, es muy posible que sea necesario cambiar la configuración del encoding a ISO-8859-1 en la sección de maven-compiler-plugin del archivo pom.xml de nuestro proyecto:

<project>
	...
	<build>
		<plugins>
			...
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					...
					<encoding>ISO-8859-1</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>
	...
</project>

Otra opción es sacar esos caracteres del código fuente, totalmente viable si estamos hablando de comentarios, o utilizar secuencias de escape para representar los caracteres que necesitamos (por ejemplo, ‘\u00F6′ en lugar de ‘ö’)

Espero que a alguien más le sea útil este tip.

Categories: Java, Maven Tags: ,

Configurar log4j desde beans de Spring Framework

Jueves, 19 de Noviembre de 2009 jarrarte Sin comentarios

Si queremos inicializar log4j al inicializar un contexto de Spring, el framework nos provee la clase org.springframework.util.Log4jConfigurer. Para configurar un bean utilizamos la clase org.springframework.beans.factory.config.MethodInvokingFactoryBean para invocar el método estático initLogging.

Este método recibe como parámetro un String que contiene la localización del archivo de configuración de log4j. Tiene otra sobrecarga que recibe además un long que representa el período en milisegundos en la que un thread verificará si el archivo tuvo cambios, y en caso afirmativo, recargará la configuración de log4j.

El bean de Spring framework se define de la siguiente forma:

<bean id="log4jInitializer" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
   <property name="staticMethod" value="org.springframework.util.Log4jConfigurer.initLogging" />
   <property name="arguments">
      <list>
         <value>configuracion_log4j</value>
      </list>
   </property>
</bean>

El parámetro configuracion_log4j apunta a un recurso que puede estar dentro del classpath (por ejemplo, “classpath:log4j.properties“) o ser un archivo del sistema (por ejemplo “file:/home/user/sample/log4j.properties“).

Además, el archivo puede ser la configuración en forma de archivo properties o de archivo XML. Dependiendo de la extensión del archivo, el método initLogging seleccionará la forma de inicializar la configuración (utilizando DOMConfigurator o PropertyConfigurator).

Evitar la ejecución de tests o ignorar resultados al utilizar Maven

Sábado, 7 de Noviembre de 2009 jarrarte Sin comentarios

Si por alguna razón queremos evitar que Maven ejecute los tests unitarios al invocarlo, podemos agregar el parámetro “-Dmaven.test.skip=true” o “-DskipTests=true” al invocarlo, por ejemplo,

mvn jar -Dmaven.test.skip=true

Si queremos que ejecute los tests pero que ignore los resultados, es decir, que no cancele la ejecución si algún test falla, podemos agregar el parámetro “-Dmaven.test.failure.ignore=true” a la invocación a Maven.

Si tenemos tests unitarios implementados, obviamente no es lo ideal evitar su ejecución, o ignorar los fallos que tengan, pero de tanto en tanto puede ser útil.

Para los tests de integración, que por concepto quizá tengan que interactuar con otros sistemas, tenemos la opción de configurar la exclusión de ciertos archivos, y que los tests contenidos en ellos no se ejecuten automáticamente al correr los goals de test de Maven. Esto se configura en el POM del proyecto, al configurar las opciones del plugin Surefire (ejecución de tests):

<build>
	<plugins>
		[...]
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-surefire-plugin</artifactId>
			<configuration>
				<includes>
					<include>**/*.java</include>
				</includes>
				<excludes>
					<exclude>integrationTests</exclude>
				</excludes>
				<forkMode>never</forkMode>
			</configuration>
		</plugin>
		[...]
	</plugins>
</build>

Para este ejempo planteado, ejecutará todos los tests del directorio correspondiente, excluyendo los que están dentro del directorio integrationTests y sus subdirectorios.

Referencias

http://maven.apache.org/general.html#skip-test
http://maven.apache.org/plugins/maven-surefire-plugin/examples/skipping-test.html
http://maven.apache.org/plugins/maven-surefire-plugin/examples/inclusion-exclusion.html

Categories: Java, Maven Tags: ,

Catálogos web de artefactos de Maven

Lunes, 19 de Octubre de 2009 jarrarte Sin comentarios

Para los desearrolladores que utilizamos Maven como herramienta para el manejo de los proyectos y sus dependencias a librerías de terceros, generalmente nos encontramos con la necesidad de agregar una nueva dependencia, pero no tenemos claro cómo se llama el JAR (artifactId), a qué grupo pertenece (groupId) o qué versión deberíamos referenciar. Estos datos son utilizados para agregar un elemento en nuestro pom.xml.

Para encontrar los detalles de estos elementos tenemos algunas opciones en la web que nos facilitan el trabajo. MavenSearch, MVNRepository y MVNBrowser son una especie de directorios de librerías, las que indexan el repositorio central (link) en caso de MVNRepository, y el central más algunos otros para el caso de MVNBrowser (ver lista de repositorios indexados) y MavenSearch (ver lista de repositorios indexados).

¿En qué nos ayudan estas aplicaciones? Nos permiten hacer búsquedas por nombres de artefacto, nombres de grupo e inclusive, en caso de MavenSearch, nos permite utilizar comodines en esa búsqueda, por ejemplo “group:*jboss” para que retorne todos los artefactos del grupo org.jboss, jboss, etc.

Además de la funcionalidad principal, me parece interesante resaltar el POM report de MVNBrowser. Es un validador de las versiones de nuestras dependencias, en el que ingresamos un listado de artefactos tal como se representan en el pom.xml y nos retorna las actualizaciones a las versiones en uso. Por ejemplo, para una entrada así:

<dependencies>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.4</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>commons-io</groupId>
		<artifactId>commons-io</artifactId>
		<version>1.4</version>
	</dependency>
	<dependency>
		<groupId>commons-lang</groupId>
		<artifactId>commons-lang</artifactId>
		<version>2.4</version>
	</dependency>
	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.14</version>
	</dependency>
</dependencies>

Nos presenta una salida similar a la siguiente captura (le eliminé los ads): MvnBrowser

En resumen:

  • MavenSearch:
    • Permite búsquedas utilizando comodines
    • Indexa varios repositorios
    • Sitio muy rápido
  • MVNBrowser:
    • Indexa varios repositorios
    • POM report, para verificar si existen nuevas versiones de nuestras dependencias
  • MVNRepository:
    • Para un determinado artefacto, grafica los cambios de tamaño entre versiones
    • Indexa sólo el repositorio central
    • No se si es casualidad, pero me ha pasado varias veces que el sitio está bajo
Categories: Java, Maven Tags: ,