Bitsmi Blog
Creación de una image base para Raspberry Pi
El primer paso para trabajar con una Raspberry Pi siempre es copiar la imagen del sistema operativo en una tarjeta SD y configurar dicho sistema creado usuarios, configurando la red… Si se tienen múltiples dispositivos, esto significa repetir los mismos pasos una y otra vez con las configuraciones comunes.
La siguiente guía describe los pasos a seguir para la creación de la imagen base de una instalación de sistema operativo para Raspberry Pi que pueda ser instalada en múltiples dispositivos y proporcione todas las aplicaciones y configuraciones comunes a todas ellos. Esto incluye la configuración de red, la creación de una cuenta de usuario administrador que centralice la gestión del sistema y la configuración del acceso remoto para dicho usuario de forma remota a través de SSH.
Con ello se pretende ahorrar tiempo y simplificar el mantenimiento de todas las instalaciones, ya que los cambios se harán una sola vez para todos los dispositivos.
NoClassDefFoundError en la inicialización de una clase Java
Los bloques de código estático en el lenguaje de programación Java són un mecanismo de inicialización de los recursos estáticos de una clase que se ejecuta en el momento en que se interactua con dicha clase por primera vez. Un fallo producido dentro de dichos bloques estáticos puede provocar errores inesperados en la ejecución del programa. En este post se habla de una de la posibles consecuencias de un error de este tipo y de cómo puede ser identificado.
Introducción al problema
Cuando la máquina virtual de Java ejecuta el código de una aplicación, el sistema de Classloaders es el encargado de recuperar e inicializar las clases incluidas en el classpath según se van requiriendo.
El proceso de carga de una clase comprende tanto la lectura del fichero *.class correspondiente cómo la inicialización de esta, lo que incluye:
- Inicialización de miembros estáticos de la clase (variables, clases anidadas…)
- Ejecución de bloques estáticos de inicialización
Por ejemplo, en el seguiente de código define la clase StaticResource
que ejecuta un código de inicialización dentro de un bloque static
en el mismo momento en que la máquina virtual carga la clase.
public class StaticResource
{
/* Ejecutado durante la carga de la clase */
static {
System.out.println("INICIALIZACIÓN StaticResource");
// Puede lanzar una RuntimeException
initializeStatic();
}
public static String getResourceName(int index)
{
System.out.println("GET RESOURCE NAME " + index);
return "Resource " + index;
}
private static void initializeStatic()
{
throw new RuntimeException("Error en la inicialización de StaticResource");
}
}
Cómo se puede ver, es perfectamente posible que el código del bloque static lance un error. Este hecho hace que la inicialización de toda la clase falle y por consiguiente, la carga de esta si no se trata correctamente el error.
Análisis del error
Para analizar las trazas producidas por una situación así, se dispone del código expuesto abajo. En el se llama al método estático
StaticResource.getResourceName
varias veces para mostrar los efectos de un fallo de este tipo:
public static void main(String... args)
{
try {
String resourceName = StaticResource.getResourceName(1);
System.out.println("RESULT 1: " + resourceName);
}
catch(Throwable e){
System.err.println("ERROR 1: " + e.getMessage());
e.printStackTrace();
}
// ...
try {
String resourceName = StaticResource.getResourceName(1);
System.out.println("RESULT 2: " + resourceName);
}
catch(Throwable e){
System.err.println("ERROR 2: " + e.getMessage());
e.printStackTrace();
}
}
Cómo resultado de la ejecución del código anterior se obtienen las siguientes trazas:
INICIALIZACIÓN StaticResource
ERROR 1: null
java.lang.ExceptionInInitializerError
at MainProgram.main(MainProgram.java:7)
Caused by: java.lang.RuntimeException: Error en la inicialización de StaticResource
at StaticResource.initializeStatic(MainProgram.java:44)
at StaticResource.<clinit>(MainProgram.java:33)
... 1 more
ERROR 2: Could not initialize class StaticResource
java.lang.NoClassDefFoundError: Could not initialize class StaticResource
at MainProgram.main(MainProgram.java:18)
En ellas se puede observar el siguiente comportamiento:
- La primera vez que se llama al método
getResourceName
, la máquina virtual intenta cargar e inicializar la clase asociadaStaticResource
y cómo resultado del error producido en el bloque de incializació estático, se produce un error de tipojava.lang.ExceptionInInitializerError
- El error es capturado por el bloque try/catch y se prosigue con la ejecución del programa. NOTA: Aquí se ha intentado simular el caso que el error
sea suprimido por algun tipo de sistema de tratamiento de errores o por un catch silencioso, es decir, que no propague el error. Se trata de una
mala práctica hacer catch de
java.lang.Error
dado que representan errores fatales en la ejecución. - Las posteriores llamadas al método
getResourceName
dan como resultado un error de tipojava.lang.NoClassDefFoundError
. Este tipo de errores ocurre cuando una clase en particular está presente en tiempo de compilación pero no lo está en tiempo de ejecución y esto es justo lo que ha pasado: La clase ha fallado y la máquina virtual no es capaz de cargar la definición en posteriores accesos, por lo que a efectos prácticos es como si esta no existiera, aunque el mensaje que acompaña al error nos da una pista de que ha sucedido por un error en la inicialización de la clase:Could not initialize class StaticResource
.
Corregir y prevenir el error
Segun lo visto en el apartado anterior, en los casos que se produce un error de tipo java.lang.NoClassDefFoundError
debido a una inicialización fallida de una
clase, esta irá acompañada de un error de tipo java.lang.ExceptionInInitializerError
anterior. En caso de que no se pueda identificar este último en las trazas de log
de la aplicación, es posible que se tenga que revisar los diferentes niveles del código implicado buscando una posible supresión del error, cómo por ejemplo un catch silencionso,
o bien añadir nuevas trazas de log que permitan descubrir la existencia del error. Una vez se tiene la certeza de que se tratade un error de incialización, se deberá identificar
la causa del error y proceder en consecuencia, por ejemplo, realizando las siguientes modificaciones:
- Utilizar bloques try/catch: Si se trata de un error recuperable, se puede ejecutar el código alternativo de inicialización dentro del bloque catch
- Propagar el error adecuadamente: Si se trata de un error fatal, se debe asegurar que el error de inicialización se propague correctamente por los diferentes
niveles del
stacktrace
hasta la parte del código encargada de reportarlo e incluso permitir la finalización de la la ejecución del programa. - Registrar el error: Se debe poder identificar el primer error
java.lang.ExceptionInInitializerError
rápidamente una vez sucede y por ellos es muy importante que las trazas del error se reporten en el sistema de alertas adecuando (Fichero de log especifico, plataforma de alertas del sistema…) de una forma clara y entendible. - Revisar la idoneidad de la inicialización estática: Se debe considerar la posibilidad de mover el bloque de código problemático de la inicialización estática de la clase, por ejemplo, convirtiendolo en código no estático que se ejecute durante la instanciación de objetos de dicha clase, o bien mediante una inicialización diferida que se ejecute una sóla vez. El código de esto último se muestra a continuación:
public class StaticResource
{
public static String getResourceName(int index)
{
System.out.println("GET RESOURCE NAME " + index);
return "Resource " + index;
}
public static void initializeStatic()
{
// En el siguiente código se puede producir un error
// ...
}
}
// ...
public static void main(String... args)
{
try {
// Ejecutado una sóla vez
String resourceName = StaticResource.initializeStatic();
}
catch(Throwable e){
System.err.println("INITILIZATION ERROR: " + e.getMessage());
e.printStackTrace();
/* En este caso, si la inicialización no es correcta, no tiene sentido seguir
* y por ello se termina la ejecución de la aplicación
*/
System.exit(1);
}
// ...
String resourceName = StaticResource.getResourceName(1);
System.out.println("RESULT: " + resourceName);
}
Aplanado de estructuras de ficheros con PowerShell
En este post se muestra una implementación de copia plana de los contenidos de un árbol de directorios determinado mediante un script de PowerShell. Dicho de otra manera, el resultado de la ejecución de este script copiará en un mismo directorio destino todos los ficheros contenidos en un directorio origen y todos sus subdirectorios.
OCP7 11 – Hilos (05) – Variables atómicas y bloqueos de sincronización
En este artículo se exponen los mecanismos básicos que proporciona la plataforma estándar de Java para el acceso y actualización de variables de forma concurrente. Se tratarán los siguientes conceptos:
- Uso de variables atómicas
- Acceso a variables mediante bloqueos de sincronización
OCP7 11 – Hilos (06) – Colecciones con protección de Thread
En general las colecciones de java.util no tienen protección de thread. Para poder utilizar colecciones en modo de protección de thread se debe utilizar uno de los siguientes mecanismos:
- Utilizar bloques de código sincronizado para todos los accesos a una colección si se realizan escrituras.
- Crear un envoltorio sincronizado mediante métodos de biblioteca como
java.util.Collections.synchronizedList(List<T>)
. Es importante destacar que el hecho de que una Collection se cree con protección thread no hace que sus elementos dispongan de la misma protección de thread. - Utilizar colecciones dentro de java.util.concurrent.
Autenticación en Bitbucket mediante SSH
Esta mini guia expone los pasos a seguir para configurar el acceso en Bitbucket mediante SSH, de forma que no sea necesaria la especificación de las credenciales cada vez que se realice una acción sobre un repositorio hospedado en dicho servicio. Incluye la configuración necesaria para los 2 tipos de repositorio soportados por Bitbucket Git y Mercurial).
Es importante mencionar que la guia está enfocada a entornos Windows aunque los pasos son bastante similares en entornos Linux cambiando las instrucciones de consola por las del entorno de que toque.
Anidar listas de objetos de negocio en un modelo Json mediante Swagger
Implementación
Para poder anidar distintos objetos de negocio dentro del Swagger, el primer paso consiste en añadir éstos en la sección definitions del Swagger.
Aquí deben declararse todos los que se van a utilizar para crear la lista de objetos de tipo Element según el ejemplo que se ha desarrollado.
Una vez añadidas las definiciones se añade al Swagger el siguiente código:
ElementList:
type: array
description: Elements List.
items:
$ref: '#/definitions/Element'
Type
indica a Swagger que el elemento contenido es de tipo array.
Description describe la lista de elementos de la lista.
items:
$ref: '#/definitions/Element'
Con esta sintaxis se indica a Swagger que cada elemento de la lista ElementList
se corresponde con una definición de objeto de negocio cuya definición també aparecerá cuando Swagger
muestre el modelo general.
Se pueden anidar varios niveles en la creación de un objeto complejo.
En el caso de ejemplo que se describe en el apartado siguiente se puede visualizar tres niveles: ElementList -> Element
que contiene a su vez listas del tipo Acces
e Invoice
.
Error Handshake Failure al establecer una conexión SSL
Descripción de error
Se produce un error de tipo Handshake Failure (detalle del tipo de error en este enlace) cuando se intenta establecer una conexión con un servidor externo a través del protocolo SSL.
OCP7 11 – Hilos (02) – Control de Errores Inesperados
La clase Thread
cuenta con un mecanismo de control de errores para casos en que se produzca un final inesperado a la ejecución de éste.
A través del método setUncaughtExceptionHandler
de la clase Thread
es posible recoger la causa de esta finalización anómala dentro del hilo principal de la aplicación y actuar en consecuencia.
OCP7 08 – Aserciones
La aserción es un mecanismo que permite comprobar suposiciones en el código que ayudan a confirmar el buen funcionamiento del mismo y que este está libre de errores. En el siguiente post se muestra su funcionamiento básico y las situaciones en las que es apropiado su uso y en las que no. Una expresión de tipo aserción se identifica por la palabra clave assert. Su sintaxis es la siguiente:
assert <expression> [: <message>]
Where:
- expression: Expresión booleana que indicará si la suposición se cumple o no. En caso de que no se cumpla, se lanzará un error de tipo
AssertionError
- message: Opcional. Si se indica un valor en la expresión, este será adjuntado en el error
AssertionError
producido.