Java Platform Debug Architecture

La Java Platform Debug Architecture o JPDA es la arquitectura que implementa los mecanismos de debug dentro de la JVM. Se trata de una arquitectura multicapa formada por varios componentes:

  • Java Debug Interface (JDI): Define la interfaz necesaria para implementar debuggers que se conectarán a la JVM a fin de enviar las diferentes ordenes que se produzcan durante la sesión de debug. Las funcionalidades de debug de los diferentes IDE son ejemplos de front-end que implementan esta interfaz.
  • Java VM Tooling Interface (JVM TI): Define los servicios que permiten instrumentalizar la JVM. Esto va desde la monitorización, la manipulación de código en caliente, la gestión de threads de ejecución, el debug de código y otras muchas funciones más. Todo ello se consigue mediante el uso de los denominados agents nativos que es especifican durante el arranque de la JVM mediante el flag -agentlib:<nombre librería>. En el caso concreto del debug, se utiliza el JWPD agent para procesar las peticiones que se envían desde el front-end del debugger. Los servicios implementados por el JWPD agent en esta capa forman el back-end del debugger que se conecta directamente con el proceso en ejecución de la JVM que está siendo debugado.
  • Java Debug Protocol (JDWP): Define el protocolo de comunicación entre los procesos front-end y el back-end del debugger a través de varios canales de comunicación que incluyen socket y memoria compartida.

En este artículo se muestra una visión práctica de como configurar la conexión a la JVM para poder las depurar aplicaciones que se ejecuten sobre ella.

Seguir leyendo Java Platform Debug Architecture

Discusión: Fork vs Branch en Git

Conceptos

La diferencia conceptual entre forking y branching viene dada por desarrollo divergente vs convergente:

  • Concepto de Forking
    Se refiere al proceso de generar una copia exacta del repositorio origen a uno nuevo en ese instante temporal. Es una copia física real y diferente, la operativa surge para realizar separaciones reales y crear nuevas lógicas bajo una base común, se asume que es poco probable que vuelvan a reunirse con el parent.

  • Concepto de Branching
    Se refiere a generar un copia del repositorio dentro del mismo repositorio origen, un pointer. Las ramas son espacios temporales sobre los que cuales realizar desarrollos nuevos o cambios. Su objetivo es volver a converger con el repositorio siempre.

La diferencia conceptual es el scope de la copia (copia separada del parent o dentro de éste) y vida (vida independiente contra tiempo de vida efímero).

La razón de la diferencia parece ser la necesidad de controlar quién puede o no realizar push de código a la rama principal, la práctica del forkeo es más común en el open source cuando los posibles colaboradores no tienen permisos sobre el repositorio original, de ahí la copia que genera otro repositorio de facto y luego la posibilidad de converger con el parent o no.

Diferencias

  1. Forking es más costoso al tener que comparar dos codebases una contra la otra, ya que el fork representa una copia literal del repositorio original (doble de espacio de almacenamiento).

  2. Branching sólo añade una rama sobre el árbol actual, el tamaño de la rama viene ligada literalmente a los cambios de ésta.

  3. Forking ofusca más ver en que anda trabajabando el equipo, al tener que moverse entre repositorios distintos en vez de sobre ramas sobre uno solo repositorio.

  4. Forking al no ser un workflow colaborativo, los cambios residen en la copia de cada uno y puede llevar a mayores problemas a la hora de mergear, perecer (políticas internas de la casa para autoborrado de forks por falta de uso para liberar espacio…) o pérdida de conocimiento.

  5. Branching al centralizar el workflow sobre un sólo repositorio permite, al actualizar sus copias, 1 remote recibir el estado de todos los remotos de las features de sus compañeros.

Why?

The Bitbucket team recommends branching for development teams on Bitbucket.

— Bitbucket

[…]People refer to Git’s branching model as its “killer feature,” and it certainly sets Git apart in the VCS community. Why is it so special? The way Git branches is incredibly lightweight, making branching operations nearly instantaneous, and switching back and forth between branches generally just as fast. Unlike many other VCSs, Git encourages workflows that branch and merge often, even multiple times in a day.

Pro Git Book by Scott Chachon and Ben Straub

Aparte de una diferencia de estilo de trabajo, de que conceptualmente los forks son para otra cosa, y el coste real es espacio en disco y tiempo de copia… en realidad ambas operativas son similares e incluso complementarias, no excluyentes.

Razón

La razón para utilizar únicamente branches es:

  1. Una forma más rápida y cómoda de recorrer el código (1 repositorio, 7 ramas de feature; en vez de bajarse 7 forks y sin saber si existirán más ramas dentro del fork).
  2. Implementar GitFlow o una aproximación a éste en nuestra forma de trabajar de forma más realista.

Requerimientos

Para poder empezar a trabajar con branches sin repercutir en la productividad deben tenerse en cuenta los siguientes pasos:

  1. Plan fijado de sobre como implementar la CI en las branches.
    • Protección de ramas a commit de developers.
      • hotfix branches, quién, cómo se crean y cierran.
      • release branches.
      • master es productivo.
      • preproducción queda atada a release branches o se puede añadir una branch específica para preproducción sobre cada release.
  2. Pipelines de la CI/CD.
    • Activación automática de la CI en branches.
      • En vez de activación por commit en master de los forks, que se activen también por commit en branches.
      • Para reducir ejecuciones podría restringirse la ejecución manual de la CI en las ramas feature.

Refs

Se puede descargar este post mediante este enlace sod_branching-vs-forking

Gestión de máquinas virtuales a través de Vagrant

Vagrant es una herramienta proporcionada por la empresa HashiCorp que permite la construcción y la gestión de máquinas virtuales de forma configurable y reproducible. Esto significa que se podrá crear un arquetipo que defina qué los componentes que forman la máquina virtual y de cómo se ejecuta esta que fácilmente se podrá distribuir para su reproducción en diferentes entornos.

Un caso de uso básico es la creación de una máquina virtual con todos los componentes de un entorno de desarrollo destinado a que los miembros de un equipo ejecuten de forma local, cada uno con su instancia propia. La distribución del arquetipo de definición de la máquina virtual o box permitirá a cada uno de ellos disponer de un entorno estandarizado igual para todos ellos. Con esto se consigue ahorrar tiempo y evitar errores, dado que la configuración sólo se realiza una única vez. También se consigue propagar los cambios que se realicen en dicho entorno de una forma ordenada y documentada, ya que los cambios se realizan directamente en el arquetipo, que además puede ser gestionado por un sistema de control de versiones.

En el presente artículo se presenta una guía inicial de la gestión de máquina virtuales mediante Vagrant, desde la creación de la box, su ejecución y gestión de los recursos asociados.

Seguir leyendo Gestión de máquinas virtuales a través de Vagrant

OCP7 11 – Hilos (04) – ReentrantReadWriteLock

Nota: Los ejemplos mostrados en este artículo suponen la utilización de arquitecturas de CPU que soportan operaciones de definición y comparación atómicas (operaciones Compare And Swap), como por ejemplo los procesadores x86 o Sparc actuales. En este caso las operaciones lock / unlock serán operaciones no bloqueantes. Otras arquitecturas que no soporten esta funcionalidad pueden requerir alguna forma de bloqueo interno por parte de la plataforma.

Paquete java.util.concurrent.locks

El paquete java.util.concurrent.locks es un marco para bloquear y esperar condiciones que es distinto de las supervisiones y sincronización incorporadas.

Bloqueo de varios lectores y un único escritor

    public class ShoppingCart {
    private final ReentrantReadWriteLock rw1 = new ReentrantReadWriteLock();

    public void addItem (Object o) {

    rw1.writeLock().lock();
    //  source code to modify shopping cart
    rw1.writeLock().unlock();
    }
    }  // End class ShoppingCart

El código fuente en main() que implementa el bloqueo y el desbloqueo es el siguiente junto con el código fuente para realizar las modificaciones del shopping cart:

    rw1.writeLock().lock();
    //  source code to modify shopping cart
    rw1.writeLock().unlock();

realizando el bloqueo de escritura.

Describiendo un poco todo lo documentado, una de la funciones del paquete java.util.concurrent.locks es la implantación de un bloqueo de varios lectores con un único escritor.

Es posible que un thread no tenga ni obtenga un bloqueo de lectura mientras está en uso el bloqueo de escritura.

Varios threads pueden adquirir simultáneamente el bloqueo de lectura pero sólo uno de ellos puede adquirir el bloqueo de escritura (n -lecturas <-> 1 escritura).

El bloqueo es reentrante: un thread que ya adquirido el bloqueo de escritura puede llamar a métodos adicionales que también obtengan el bloqueo de escritura sin miedo a que se produzca un bloqueo.

Bloqueo de lectura (sin ningún escritor)

Ejemplo dónde se muestra como los métodos de lectura son concurrentes:

    public class ShoppingCart {
        public String getSummary() {
            String s = "";
            rw1.readLock().lock();
            //  Source code to read cart, modify s
            rw1.readLock().unlock();
            return s;
        }

        //  Todos los métodos de sólo lectura se pueden ejecutar de forma simultánea
        public double getTotal () {
            //  another read-only method
        }
    }   //  End class ShoppingCart

En el ejemplo todos los métodos determinados como de sólo lectura pueden agregar el código necesario para bloquear y desbloquear un bloqueo de lectura.

ReentrantReadWriteLock permite la ejecución simultánea de ambos, dónde puede ejecutar un único método de sólo lectura o varios métodos de sólo lectura concurrentes.

CRLF end of line problems in Git

Sometimes a new problem could appear regarding the end of line on files using an IDE (like IntelliJ for example) when doing a Commit&Push into Master through Git.

When this situation happens a new window may popup:

IntelliJ Line Separators Warning

It is recommended the use of the option "commit as it is" by default.

Be sure to uncheck the setting ‘Warn if CRLF line separators are about to be commited to avoid the warning popup‘ in case of using the IntelliJ IDE.

To prevent git from automatically changing the line endings on your files in general is enough running this command:

git config --global core.autocrlf false

BUt a general solution that force one customized configuration is the creation of a new file in the root folder of the project called .gitattributes.

This is its content:

* text eol=crlf working-tree-encoding=UTF-8
*.java text eol=crlf working-tree-encoding=UTF-8
*.xml text eol=crlf working-tree-encoding=UTF-8
*.properties text eol=crlf working-tree-encoding=UTF-8
*.jsp text eol=crlf working-tree-encoding=UTF-8
*.sql text eol=crlf working-tree-encoding=UTF-8

It’s important to point out that this configuration can be changed and adapted to a different one depending on the necessities of the project.

More references

  1. Git documentation here

  2. Git attributes depending on the programming language can be found here.

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.

Seguir leyendo Creación de una image base para Raspberry Pi

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.

Seguir leyendo NoClassDefFoundError en la inicialización de una clase Java

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.

Seguir leyendo Aplanado de estructuras de ficheros con PowerShell

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

Seguir leyendo OCP7 11 – Hilos (05) – Variables atómicas y 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.

Recopilaciones simultáneas(ConcurrentLinkedQueue)

La clase ConcurrentLinkedQueue proporciona una cola FIFO no bloqueante con protección de thread escalable eficaz.

Adicionalmente existen cinco implementaciones en java.util.concurrent que también soportan la interfaz ampliada BlockingQueue que define las versiones de bloqueo de colocación y captura:

Ademas de las colas, este paquete proporciona implementaciones de Collection diseñadas para su uso  en contextos multihilo siguientes:

Cuándo  se espera que muchos threads accedan a la colección proporcionada, normalmente se prefiere

  • ConcurrentHashMap a HashMap sincronizado.
  • ConcurrentSkipListMap a TreeMap sincronizado.

Cuándo el número esperado de lecturas y transversales supera en gran medida el número de actualizaciones en una lista se prefiere

  • CopyOnWriteArrayList a ArrayList sincronizado .