Weblogs Código

Blog Bitix

Las aplicaciones integradas del entorno de escritorio GNOME

febrero 16, 2020 10:00

Todos los entornos de escritorio poseen unas pocas aplicaciones básicas y sencillas pero de uso muy común para todos los usuarios. Estas aplicaciones del entorno de escritorio están estrechamente integradas para ofrecer una experiencia de usuario consistente y funcionar correctamente entre ellas. GNOME posee varias desde un editor de archivos de texto, una calculadora, captura de pantalla, visor de imágenes, reproductor de música y vídeo, gestor de correo electrónico, calendario, navegador web, juegos, … y otras más.

GNOME

Todos los equipos informáticos incorporan un sistema operativo, este es una pieza importante e indispensable de software que abstrae las las especificidades de cada elemento hardware además de permitir se uso compartido desde la CPU, memoria, almacenamiento persistente, comunicación de red entre otros muchos componentes y periféricos que poseen los ordenadores actuales. Pero simplemente ofrece una interfaz de bajo nivel destinada a los programas no el usuario.

Para facilitar el uso el sistema operativo y en los actuales ofrecer una interfaz gráfica a través de ventanas, imágenes y texto a los usuarios están los entornos de escritorio. Los entornos de escritorio además incorporan una colección de aplicaciones de uso común para todos los usuarios como un navegador de archivos, navegador web, editor de texto, reproductor de música, reproductor de vídeo, visor de imágenes y documentos, gestor de aplicaciones, calculadora, capturador de pantalla entre las básicas.

Capas de software desde el hardware hasta el entorno de escritorio

Capas de software desde el hardware hasta el entorno de escritorio

Ejemplos de sistema operativos son GNU/Linux, Windows, macOS o FreeBSD. En los casos de Windows y macOS el mismo nombre engloba la interfaz gráfica y el entorno de escritorio sin ofrecer ninguna alternativa, en GNU/Linux hay varias alternativas de entorno de escritorio a elegir según las preferencias del usuario entre ellas GNOME, KDE, XFCE, Pantheon de elementary OS, MATE, Cinnamon o LXDE.

Las distribuciones de GNU/Linux proporcionan el conjunto de software completo formado por el núcleo o kernel que con el conjunto de aplicaciones de GNU forman el sistema operativo, el entorno de escritorio con sus aplicaciones básicas y finalmente aplicaciones adicionales que no forman parte del entorno de escritorio pero son preinstaladas en la instalación del sistema. Algunos ejemplos de distribuciones GNU/Linux son Ubuntu, Fedora, openSUSE, Debian, Arch Linux o elementary OS entre muchas otras.

Ubuntu openSUSE Debian

Arch Linux Fedora Elementary OS

El entorno de escritorio se instala con el sistema con lo que primero es elegir una distribución GNU/Linux, para los usuarios que proviene de Windows o macOS y quieren probar GNU/Linux una distribución recomendable es Ubuntu. El siguiente paso seguir la guía de como instalar Ubuntu paso a paso desde cero y conocer las tareas básicas de administración y uso después de instalar una distribución. Para los usuarios que ya conocen GNU/Linux, quieren personalizar el sistema con sus preferencias no las de los desarrolladores de la distribución y prefieren un modelo de ciclo de vida rolling release en el que el software se mantiene en constante actualización con las últimas versiones puede instalar Arch Linux de forma rápida, desatendida y personalizable con un scipt.

El entorno de escritorio GNOME

GNOME con las críticas iniciales a la versión primera de la rama 3.0 publicada en abril del 2011 por el cambio significativo respecto a versiones anteriores ha mejorado mucho y sigue haciéndolo con cada nueva versión publicada cada seis meses.

En GNOME se opta por la simplicidad, a veces criticada por la falta de opciones de configuración y personalización, en la que los detalles gráficos y la usabilidad del sistema son aspectos con más relevancia respecto a versiones anteriores.

Continúan realizándose mejoras, algunas de las cuales son aplicadas en cada nueva versión y otras registradas para tenerlas en cuenta en futuras versiones.

Escritorio de GNOME Lanzador de aplicaciones de GNOME Lanzador de aplicaciones de GNOME

Entorno de escritorio y lanzador de aplicaciones de GNOME

Aplicaciones del entorno de escritorio GNOME

GNOME integra un conjunto de aplicaciones que proporcionan una funcionalidad importante en la experiencia del entorno de escritorio.

Estas aplicaciones están diseñadas por los propios diseñadores de GNOME como un paquete coherente, son parte de la experiencia GNOME, están diseñadas para funcionar de forma cooperativa unas con otras, tienen una integración fuerte con el entorno de escritorio, tienen nombres genéricos y son exclusivas de la experiencia GNOME.

El conjunto de aplicaciones del núcleo o esenciales de GNOME está formado por unas 30 agrupadas en diferentes categorías.

Conversaciones y organización personal

  • Evolution: cliente de correo electrónico y organizador
  • Geary: cliente de correo electrónico
  • Contactos
  • Calendario

Evolution Geary

Contactos Calendario Calendario

Archivos

  • Visor de imágenes
  • Fotos
  • Documentos
  • Gestor de archivos (compresor)
  • Música
  • Lollypop: reproductor de música
  • Vídeos

Visor de imágenes Fotos Fotos

Documentos Archivos

Música Música Música

Lollypop

Vídeos Vídeos Vídeos

Herramientas de sistema

  • Captura de pantalla
  • Discos
  • Ayuda
  • Trazas
  • Informar de problema
  • Terminal
  • Uso (system monitor + disk usage)

Captura de pantalla Discos Ayuda de GNOME

Terminal Terminal Monitor del sistema

Sistema esencial

  • Configuración: opciones de configuración del sistema
  • Software: instalar, actualizar y desinstalar programas de software
  • Web: navegador de páginas web

Configuración Configuración Configuración

Software Software

Web Web Web

Mundo

  • Relojes
  • Tiempo
  • Mapas

Relojes Tiempo

Mapas Mapas Mapas

Mapas

Utilidades

  • Calculadora
  • Tipografías
  • Notas
  • Editor de texto
  • Retoques: ofrece varios opciones de personalización
  • Herramientas de red

Calculadora Calculadora

Tipografias Tipografias

Notas Editor de texto Retoques

Herramientas de red

Propósito especial

  • Cajas: virtualización

Cajas Cajas Cajas

Audio y vídeo

  • Cheese: aplicación webcam
  • Sound Juicer: extractor de CD de audio
  • Grabadora de sonido

Cheese Grabadora de sonido

Creación y edición

  • Brasero: grabadora de CD y DVD
  • EasyTAG: editor de metadados de archivos de música
  • Subtítulos: editor de subtítulos para archivos de vídeo

Comunicación

  • Fractal
  • Polari

Entornos de desarrollo

  • Builder: entorno integrador de desarrollo
  • Glade: diseñador de interfaces gráficas de GNOME

Builder Builder Builder

Builder Glade Glade

Juegos

Aventura
  • MUD
  • Arcade
  • Nibbles
  • Robots
  • Blocks
  • Quadrapassel (Lines)
  • Board
  • Chess
  • Iagno
  • Mahjongg
Cartas
  • Aisleriot (Solitaire, sol)
  • Tali
Emuladores
  • Juegos: emulador de juegos
Lógica
  • 2048
  • Atomix
  • Five or more
  • Four in a row
  • gbrainy
  • Hitori
  • Klotski
  • Lights off
  • Mines
  • Sudoku
  • Swell Foop
  • Taquin
  • Tetravex

Juegos

Ajedrez Klotski Minas

Sudoku Swell Foop Tetravex

Formatos de vídeo y audio y aplicaciones de terceros

Para que las aplicaciones de vídeos y música soporten más formatos de archivo es necesario instalar los decodificadores de esos formatos. En Arch Linux los siguientes paquetes.

Códecs no encontrados en Vídeos

1
$ sudo pacman -S gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav
pacman-gstreamer.sh

Las aplicaciones de terceros proporcionan funcionalidades adicionales, son una alternativa a las de GNOME con más funcionalidades o complementan a estas que algunos usuarios necesitan como paquete ofimático con procesador de documentos, hoja de cálculo y presentaciones, edición de vídeo, editor de fotos, editor de imágenes vectoriales, editor de animación 3D, gestor de biblioteca de libros electrónicos, descargas de archivos, mensajería instantánea, videoconferencia, nube privada, otro reproductor de audio y vídeo, conversores entre formatos de audio y vídeo, compiladores, bases de datos, gestor de contraseñas, …

En otro artículo recojo un listado de programas básicos según categoría en GNU/Linux de terceros que no son específicos de ningún entorno de escritorio. Todos esos programas son software libre sin coste de licencia de uso para cualquier propósito incluyendo que sea personal o empresarial con fines lucrativos.

» Leer más, comentarios, etc...

Blog Bitix

El recolector de basura de Java, que hace y como funciona en cada versión

febrero 15, 2020 12:00

El recolector de basura o garbage collector es una de las piezas fundamentales del lenguaje Java y su plataforma. Una ventaja sobre lenguajes que no poseen reflectores de basura y que se ha adoptado por los lenguajes desarrollados en la últimas décadas. Facilita a los programadores la creación de programas, una mayor productividad, evita errores y fallos de seguridad.

Java

Den entre las características de Java ¿a qué se debe su popularidad? ¿al lenguaje simple de fácil lectura sin crípticas expresiones? ¿a ser multiplataforma, write once, run everywhere? ¿a la máquina virtual JVM que lo hace independiente de la plataforma sistema operativo y soporta múltiples lenguajes compilados a bytecode? ¿a su extensa y completa documentación Javadoc de cada clase incluida en el JDK? ¿a las clases incluidas en el JDK con una completa librería para trabajar con colecciones, interfaces gráficas o conexión a bases de datos? ¿a mantener la compatibilidad hacia atrás de modo que programas escritos hace 20 años sigan compilando y funcionando en versiones más recientes de la máquina virtual?

Además de todas las anteriores entre las principales hay que añadir la recolección de basura que libera al programador la gestión de la memoria, tanto para solicitarla como para devolverla al sistema. Lenguajes más antiguos y con otros propósitos más cercanos a la programación de sistema donde prima el rendimiento y el acceso cercano al hardware como C no poseen recolector de memoria y requieren que el programador solicite de forma explícita con la función malloc la memoria y el tamaño de la memoria a reservar y la libere también cuando se deja de usar de forma explícita con la llamada a la función free.

Esto para cada dato y en un programa grande serán muchos supone una dificultad añadida a la creación y mantenimiento. Este ejemplo en código C muestra el uso de la función malloc con la que el programa solicita memoria al sistema operativo y con free la libera.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *ptr;

    // allocate memory
    ptr = (int*) malloc(1 * sizeof(int));

    // if memory cannot be allocated
    if(ptr == NULL)
    {
        printf("Error! memory not allocated.");
        exit(0);
    }

    printf("Enter a number: ");
    scanf("%d", ptr);

    // print the number
    printf("Number = %d\n", *ptr);
    
    // deallocating the memory
    free(ptr);

    return 0;
}
main.c
1
2
3
4
5
[picodotdev@archlinux ~]$ gcc main.c -o main
[picodotdev@archlinux ~]$ ./main
Enter a number: 7
Number = 7
[picodotdev@archlinux ~]$
main.out

Como desarrollador de Java apenas hay que preocuparse de fugas de memoria ni de fallos en el programa por liberar memoria antes de que dejar de usarla. En Java la solicitud de memoria al sistema se hace de forma explícita con la palabra reservada new para crear una instancia de un objeto pero no hace falta especificar el tamaño de la memoria a reservar como en C. Tampoco hace falta liberar de forma explícita el objeto cuando dejar de usarse es el propio recolector de basura el que determina si una instancia ha quedado inaccesible desde el programa y lo libera en el proceso de recolección de basura que ejecuta la máquina virtual de forma periódica y automática sin la intervención del programa.

El recolector de basura además de simplificar el código de las aplicaciones, evita fallos en tiempo de ejecución con posibilidad de que sean difíciles de depurar, evita en gran medida las fugas de memoria y fallos graves de seguridad. En los programas en C es muy común errores de seguridad por casos en los que se sobreescriben zonas de memoria contiguas por no hacer comprobaciones en los límites de los arrays, muchos boletines de seguridad CVE en muchas librerías tienen un origen de este tipo. En Java si se intenta acceder a un array fuera de sus límites se produce una excepción ArrayIndexOutOfBoundsException, el programa sigue teniendo un error pero no tiene por que terminar su funcionamiento de forma drástica porque el sistema operativo lo mata y no son posibles los fallos de seguridad por sobreescribir una zona de memoria contigua al array pero fuera de sus límites.

La desventaja de los recolectores de basura es que cada cierto tiempo requieren detener la ejecución de la aplicación para proceder a liberar la memoria dejada de usar por la aplicación. Estas pausas que suceden fuera del control de la aplicación hace que para entornos donde se necesite una respuesta bajo unos términos de tiempo bajos o extremadamente alto rendimiento como en el caso de sistemas en tiempo real hace que los recolectores de basura sean una dificultad.

En Java una de las áreas para mejorar el rendimiento y tiempo de respuesta de las aplicaciones es modificar el algoritmo de recolección de basura, para mejorar el tiempo que necesita para ejecutarse y número de pausas además de posibilitar el paralelizar la ejecución del recolector de basura con la ejecución de la aplicación. A lo largo de los años en Java ha habido varios recolectores de basura.

Salvo casos en los que hay que ajustar al límite la máquina virtual en aplicaciones que necesitan gran rendimiento no es necesario preocuparse por el funcionamiento del recolector de basura, hace su cometido como se espera. En mis años de experiencia nunca he tenido que configurarlo, pero es interesante conocer que mejoras se van implementando en cada nueva generación de algoritmo. En la mayoría de los casos parece que el sucesor se basa en el anterior y aporta alguna mejora.

En la revista JavaMagazine se han publicado varios artículos explicando el recolector de basura de Java. En las secciones de las diferentes versiones de recolectores de basura resumo parte del contenido de esos artículos.

Otros artículos relativos a la recolección de basura y su configuración son los de la Garbage Collection Tuning con una explicación más detallada.

Cómo funciona el recolector de basura

En un lenguaje orientado a objetos como Java los datos están contenidos en los objetos. Los objetos son almacenados en el espacio de memoria del sistema denominado heap distinta a la memoria del código ejecutable del programa, datos para las constantes y de las pilas de memoria para los argumentos y valores de retorno entre métodos.

Las clases de colecciones de Java contienen referencias a objetos. Un ejemplo podría ser el siguiente de un árbol binario.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class TreeNode {

    private TreeNode left;
    private TreeNode right;
    private int data;

    TreeNode(TreeNode l, TreeNode r, int d) {
        this.left = l;
        this.right = r;
        this.data = d;
    }

    public void setLeft(TreeNode l) { 
        left = l;
    }

    public void setRight(TreeNode r) {
        right = r;
    }
}
TreeNode.java

Al insertar nodos todos los objetos insertados están accesibles.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
...

public class Main {

    public static void main(String[] args) {
        TreeNode left = new TreeNode(null, null, 13);
        TreeNode right = new TreeNode(null, null, 19);
        TreeNode root = new TreeNode(left, right, 17);
        ...
        root.setRight(new TreeNode(null, null, 21));
    }
}
Main.java

Objetos al inicializar la estructura de datos

Objetos al inicializar la estructura de datos

Al realizar la operación de eliminación de un nodo del árbol el objeto eliminado del árbol deja de ser accesibles para el programa sino hay más referencias en otras estructuras de datos con la que alcanzar a ese objeto y pasa a ser reclamable por el recolector de basura.

Objetos después de eliminar un nodo

Objetos después de eliminar un nodo

Con más operaciones los objetos no accesibles aumentan. Estos objetos no accesibles siguen consumiendo memoria, el recolector de basura se encarga de liberar la memoria de esos objetos y después compactar la memoria en uso y la liberada queda utilizable para nuevas instancias de objetos.

Objetos después de realizar múltiples operaciones de inserción

Objetos después de realizar múltiples operaciones de inserción

Al realizar la operación de compactar la memoria los objetos cambian de ubicación y el programa debe conocer la nueva ubicación, esto requiere actualizar las referencias de los objetos almacenados en las estructuras de datos. La forma fácil de realizar la liberación de memoria y la compactación es parar los threads de la aplicación, liberar la memoria, compactarla y actualizar todas las referencias de los objetos a la nueva ubicación, después reiniciar la aplicación. Esta parada de la aplicación se conoce como stop-the-world. Sin embargo, el parar la aplicación reduce el rendimiento, esto no es deseable.

Para reducir las pausas de los recolectores de basura hay dos estrategias:

  • Los algoritmos concurrentes: realizar el trabajo mientras funciona la aplicación, la aplicación no necesita pausas ni sufre pérdida de rendimiento.
  • Los algoritmos paralelos: emplear más threads para hacer el trabajo más rápido, aumenta el rendimiento del recolector de basura.

El recolector de basura por defecto en Java 8 usar la estrategia paralela, usa varios threads para tener un alto rendimiento. Otras versiones de algoritmos emplean ambas técnicas simultáneamente para tener un alto rendimiento y apenas sin pausas. Hay dos áreas de mejora en los algoritmos de recolección de basura y medir su desempeño. La primera es el rendimiento, cuanta cantidad de tiempo de CPU de la aplicación es gastada en realizar recolección de basura en vez de ejecutar código de la aplicación. La segunda es el tiempo de latencia en las pausas.

Recolector de basura Parallel

El recolector de basura parallel emplea zonas para segregar los objetos, la zona de objetos jóvenes y la zona de objetos viejos. Inicialmente los objetos se crean en la zona de objetos jóvenes, cuando han sobrevivido a varios ciclos del recolector de basura son movidos a la zona de objetos viejos.

La razón es que en vez de recolectar los objetos de toda la memoria hay más probabilidad de recolectar más objetos que han dejado de usarse en la zona de objetos jóvenes. Eventualmente también es necesario recolectar los objetos de la zona de objetos viejos.

Es el recolector de basura por defecto en Java 8 y anteriores. La opción para usar este recolector de basura es la siguiente.

1
-XX:+UseParallelGC
java-option-gc-parallel.txt

Recolector de basura Garbage First o G1

El recolector de basura G1 usa ambas estrategias la paralela y la concurrente. Usa threads concurrentes mientras la aplicación está funcionando buscando los objetos vivos y usa la estrategia paralela para realizar la recolección y compactación rápidamente manteniendo las pausas bajas.

El recolector de basura G1 también divide la memoria en regiones de memoria catalogadas como de objetos jóvenes y objetos viejos. Las regiones de objetos jóvenes las recolecta en cada pausa, para las zonas de objetos viejos tiene cierta flexibilidad para recolectar muchas o pocas como la estimación de tiempo que le llevará hacerlo le permita para cumplir con el objetivo de tiempo de pausa configurado dado que permite ajustar según preferencia el límite de tiempo máximo deseado para las pausas.

División por zonas de G1 y compactación de objetos

División por zonas de G1 y compactación de objetos

G1 conoce cuantos datos vivos hay en cada región, lo calcula con la estrategia concurrente mientras la aplicación está funcionando, y el tiempo aproximado que consume copiar los datos vivos. Si se prefieren pausas bajas por el tiempo de pausa configurado G1 puede elegir evacuar solo unas pocas regiones. Si las pausas pueden ser mayores G1 puede elegir mayor número de regiones. Esta flexibilidad le permite a G1 liberar primero las zonas de objetos viejos en las que estime que liberará más objetos dado que conoce cuantos objetos siguen vivos.

La contrapartida de especificar pausas bajas es que G1 puede no ser capaz de mantener el ritmo de liberación de memoria, en cuyo caso eventualmente opta por parar la aplicación con el modo stop-the-world. Esto implica que el proceso de búsqueda de objetos vivos y el proceso de copiado es realizando mientras los threads de la aplicación están parados. Si G1 no puede cumplir con el objetivo de tiempo de pausa en recolecciones parciales, entonces el recolector de basura necesitará una pausa de mayor tiempo que el límite máximo deseado especificado.

G1 en general es un recolector con un buen balance entre rendimiento y restricciones de tiempo de pausa. Es el recolector de basura por defecto en Java 9.

1
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
java-option-gc-g1.txt

Recolector de basura Shenandoah

Usa la misma disposición de regiones que G1 y usa el mismo sistema de escaneo concurrente para calcular la cantidad de objetos vivos en cada región. Difiere en que la compactación también es concurrente, de modo que no necesita limitar el número de regiones a recolectar para minimizar los tiempos de las pausas.

La dificultad para Shenandoah es que la copia concurrente se realiza al mismo tiempo que los threads de la aplicación están accediendo al objeto de modo que ambos deben estar de acuerdo en donde está el objeto. La dirección del objeto puede estar en otros varios objetos y la actualización debe realizarse simultáneamente.

La solución que aplica es una indirección. Los objetos son reservados en memoria con espacio extra para un puntero de indirección. Cuando los threads de Java acceden al objeto leen primero el puntero de indirección para ver donde se ha movido el objeto. Cuando el recolector de basura mueve el objeto, actualiza el puntero de indirección a la nueva localización. Los objetos nuevos tienen un puntero de indirección que apunta a si mismos. Solo cuando el objeto es copiado durante la recolección de basura el puntero de indirección apunta otro sitio. Si el programa Java modifica los datos de un objeto que Shenandoah está copiando, se produce un problema de concurrencia que es solventado haciendo que los threads de la aplicación cooperen con los threads del recolector de basura.

Shenandoah elimina la necesidad de realizar pausas durante la compactación de modo que las pausas cuando se hacen son mucho menores. El recolector de basura Shenandoah es un proyecto de OpenJDK que forma parte del OpenJDK 12 y está siendo portado al JDK 8 y 11. Se puede activar en Java 12 con la siguiente opción de la máquina virtual.

1
-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
java-option-gc-shenandoah.txt

Recolector de basura ZGC

Para los algoritmos que realizan pausas incrementar la memoria heap mejora el rendimiento dado que el número de pausas es menor por la menor necesidad de liberar memoria pero hace que las pausas sean más largas porque hay más trabajo que realizar dado que la memoria total es mayor.

Los objetivos principales de ZGC son baja latencia, escalabilidad y facilidad de uso. Para conseguirlo todas las operaciones de recolección de basura se realizan de forma concurrente mientras la aplicación continúa ejecutándose salvo algunas excepciones. Escala desde unos cientos de megabytes de memoria a memorias de tamaño de terabytes manteniendo consistentemente tiempos bajos de pausas menores de entre 10 y 2 ms.

Los recolectores de basura anteriores y hasta ahora necesitaban realizar pausas stop-the-world para algunas operaciones de recolección de basura. Para un recolector de basura de baja latencia esto es problemático de modo que ZGC realiza todas las operaciones concurrentemente a la aplicación de modo que no hay apenas latencias.

Comparación de latencia entre ZGC, Parallel y G1

Comparación de latencia entre ZGC, Parallel y G1

ZGC se puede activar en Java 13 con la siguiente opción para la máquina virtual.

1
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
java-option-gc-zgc.txt

» Leer más, comentarios, etc...

Navegapolis

Por qué pusimos en marcha un registro de propiedad intelectual

febrero 13, 2020 06:47

safe creative cc tech summit

En 2007, cuando pusimos en marcha Safe Creative, muchos expertos dudaban si era posible abrir en internet un registro de propiedad intelectual.

El mismo Creative Commos nos invitó en el  Technology Summit de 2008,  a explicar "por qué y para qué un registro de propiedad intelectual", presentándonos con un título que parecía cuestionarlo: "Developers of digital copyright registries and similar animals" :-P 

¿Por qué un registro electrónico? ¿Por qué no seguir registrando como siempre en oficinas de registro de ministerios o consejerías de educación?

Los registros de propiedad intelectual no otorgan derechos: el autor de una obra creativa tiene todos sin necesidad de registrarlos (Convenio de Berna). Lo que ocurre es que resulta muy aconsejable que registre su obra antes de "moverla", para tener asentada una primera prueba declarativa de su autoría, para publicar su trabajo o enviar versiones previas con tranquilidad, sabiendo que dispone de la merjor prueba en el tiempo frente a quien pudiera estar tentado de atribuírselo.

Y así fue. Para subir a internet la canción que hemos compuesto, el vídeo, el libro.... con la seguridad de haberlo registrado previamente, sin tener que esperar a que abra la ventanilla del registro a las 9 de la mañana para llevarles  una copia. Para eso nació Safe Creative (y para otras muchas cosas que tampoco se pueden hacer en un registro tradicional).

... que preguntárselo hace 13 años era normal, ¡pero a estas alturas!     ;-)

 

 EN EL REGISTRO TRADICIONALEN SAFE CREATIVE
     
La prueba de derechos de autoría se basa En la presunción administrativa de veracidad que la ley del país correspondiente concede al funcionario que inscribe la manifestación del autor. En la evidencia tecnológica que constituye la identificación de la obra con 3 huellas criptográficas diferentes, y de la fecha por la aplicación de un sellado de tiempo cualificado, redundado con un proceso de auditoría diaria sobre blockchain. 
     
Validez internacional La presunción de veracidad del funcionario es válida en su propio país. La peritación de pruebas o evidencias tecnológicas es válida en todas las jurisdicciones.
     
Operativa Normalmente presencial, con formatos de obras en algunos casos definidos y restringidos por reglamentos internos.

On line 24 x 7 en cualquier formato que permita identificar a la obra.

 

» Leer más, comentarios, etc...

Coding Potions

Vue slots ≫ Qué son y cómo usarlos

febrero 13, 2020 12:00

Introducción ¿Qué son los slots?

Los slots son un mecanismo de Vue JS que sirve para insertar contenido HTML dentro de los componentes. Es decir, con los props puedes pasar objetos y variables javascript a los componentes y con los slots puedes insertar contenido HTML dentro de otros componentes.

Imagina que quieres crear un componente que sirva para renderizar un header, y además quieres que dentro del header, a la derecha se puedan poner botones o otra información de forma que cambien dependiendo de la página. Esto se puede hacer con props, pero con los slots es mucho más sencillo y encima permites que desde fuera puedes pasar el contenido HTML que quieras.

Los slots sirven para insertar contenido en el componente hijo

Seguramente estés confuso, pero ahora con los ejemplos lo vas a ver mucho más claro.

Cómo crear slots en Vue

Sigamos con el ejemplo del botón que vimos en el artículo anterior sobre props y eventos en Vue. Imagina que ahora quieres poder añadir un icono que sea personalizable para cada botón. Una forma de abordarlo es creando un prop para pasar desde fuera el nombre del icono. Otra forma de hacerlo es con slots:

<template>
  <button @click="handleClick">
    <slot></slot>
  </button>
</template>
export default {
  methods: {
    handleClick() {
      this.$emit("click", this.example);
    }
  }
}
<script>
</script>

El slot es una etiqueta especial que tiene Vue. Cuando pones un slot lo que estás diciendo es que en ese punto vas a colocar contenido desde fuera. Fíjate que ahora no necesitamos poner un prop, porque todo el contenido que vaya dentro del botón se pasará desde fuera.

Veamos ahora cómo pasar contenido a los slots:

<template>
  <my-button @click="handleClick">
    <i class="fas fa-cat"></i>
    Botón de ejemplo
  </my-button>
</template>

import MyButton from "@/components/MyButton.vue";

export default {
  components: {
    MyButton
  },
  methods: {
    handleClick(info) {
      console.log("Click event on the button of the children with: " + info)
    }
  }
}
<script>
</script>

Todo el contenido que pongas dentro de la etiqueta HTML de un componente con slot se sustituirá dentro del componente en el lugar en el que esté colocada la etiqueta slot. Para el ejemplo anterior, el botón finalmente quedará como:

<button>
  <i class="fas fa-cat"></i>
  Botón de ejemplo
</button>

Pero no todo es bueno con los slots. La parte mala de los slots es que das demasiada libertad a la hora de usar el componente. Por ejemplo para el caso del botón, alguien podría poner dentro del slot una tabla por ejemplo haciendo que el botón se vea mal. Para este caso en concreto yo usaría un prop para el texto del botón y otro prop para el icono.

Más de un slot. Named slots

Otra cosa que se puede hacer con Vue es añadir más de un slot, para ello vas a tener que colocar un nombre a cada slot para poder indentificarlos:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

Con el atributo name le pones el nombre a cada slot. Para poder elegir qué poner en cada slot tienes que usar una etiqueta de Vue llamada template.

Los templates son unas etiquetas especiales de Vue que cuando se compila la página se eliminan dejando solo su contenido. En el ejemplo de abajo se colocará el contenido definido para cada slot en su sitio del componente eliminándose las etiquetas template y solo quedando los headings y los párrafos

<base-layout>
  <template slot="header">
    <h1>Header de la página</h1>
  </template>

  <p>Contenido de la página</p>

  <template slot="footer">
    <p>Footer de la página</p>
  </template>
</base-layout>

El ejemplo de arriba finalmente quedará renderizado como:

<div class="container">
  <header>
    <h1>Header de la página</h1>
  </header>
  <main>
    <p>Contenido de la página</p>
  </main>
  <footer>
    <p>Footer de la página</p>
  </footer>
</div>

Las etiquetas template han desaparecido. El slot del medio, el del contenido, no tiene nombre y por lo tanto será sustituido en el slot por defecto sin nombre.

Contenido por defecto del slot

También puedes poner contenido por defecto en caso de que no uses el slot:

<button type="submit">
  <slot>Submit</slot>
</button>

En caso de que al crear este componente botón no le pases contenido, Vue lo creará con el texto Sumbit.

Con esto puedes crear todo el contenido que quieras dentro de los slots por si al usar el componente no pasas nada dentro.

Scoped slots

Los scoped slots es de esas cosas que no los conoce mucha gente. Lo que permiten los scoped slots es poder pasar información desde el componente hijo al padre, es decir, desde el hijo pasas contenido al padre para que éste lo pueda pintar como necesite.

Ponte en el ejemplo de que tienes un componente que pinta una lista de usuarios. A este componente le pasas un prop con el array de usuarios a pintar. Pues bien, con los scoped slots, al hacer el v-for para pintar los usuarios, puedes pasar al componente padre, en el slot, el usuario que se está pintando en ese momento de tal forma de que en el padre decides cómo lo quieres pintar. Por ejemplo:

En el componente hijo:

<ul>
  <li v-for="(user, i) in users" :key="i">
    <slot v-bind:user="user"></slot>
  </li>
</ul>

Dentro del slot haces bind de la variable user que se está pintando para pasarla al componente padre.

En el componente padre:

<user-list>
  <template v-slot:default="slotProps">
    <span>{{ slotProps.user.firstName }}</span>
    <span>{{ slotProps.user.lastName }}</span>
  </template>
</user-list>

Recoges la variable del user en slotProps y pintas el usuario como necesites.

Por ejemplo, usando el mismo componente, también podrías tener a la vez en otro componente:

<user-list>
  <template v-slot:default="slotProps">
    <div>First name: </div>
    <div>{{ slotProps.user.firstName }}</div>
    <div>Last name: </div>
    <div>{{ slotProps.user.lastName }}</div>
  </template>
</user-list>

Esto es muy útil cuando quieres tener un componente de tabla o un componente que pinte una lista o colección de elementos y necesitas dos formas diferentes de pintarlos. Recogiendo el valor en los componentes padres puedes decidir cómo vas a querer renderizarlo usando un solo componente hijo.

Conslusiones

Como he dicho, yo personalmente recomiendo crear los componentes usando props y no slots porque así limitas más el contenido de los componentes para que no queden cosas extrañas. De todas formas para ciertos componentes sobre todo estructurales en los que necesitas pasar mucho contenido HTML su uso si que está más justificado.

En próximos capítulos veremos como estructurar los proyectos de Vue para poder mandar peticiones HTTP a un servidor o API y así poder conectar a las aplicaciones creadas en Vue con un servidor con bases de datos.

» Leer más, comentarios, etc...

Fixed Buffer

Escribiendo código de alto rendimiento en .Net Core

febrero 11, 2020 09:00

Tiempo de lectura: 12 minutos
Imagen ornamental para la entrada Escribiendo código de alto rendimiento en C# con .Net Core

Recientemente he estado en una charla de un grande del sector como es Carlos Landeras y en el clásico afterwork tras la charla, he tenido la oportunidad de hablar con el largo y tendido con el sobre el tema y finalmente me he decidido a escribir una entrada sobre cómo hacer código de alto rendimiento utilizando .Net Core.

En primer lugar y, ante todo, hay que utilizar la cabeza y no perder tiempo en optimizar sin ser necesario. La gran mayoría de aplicaciones no necesitan de utilizar las cosas que vamos a ver a continuación, pero es conveniente saber que existen porque a veces hacen falta.

¿Cuándo debo mejorar el rendimiento de mis aplicaciones .Net Core?

Lo primero que cabe preguntarse en este momento es si vale la pena optimizar nuestra aplicación, ¿Y cómo puedo decidir si vale la pena? La pregunta es difícil de resolver porque no hay una verdad absoluta al respecto, pero principalmente podemos hacernos algunas preguntas para decidir si necesitamos modificar nuestro código:

  • ¿Tiene una alta concurrencia?
  • ¿Los recursos del equipo son limitados?
  • ¿Estamos teniendo problemas con el rendimiento?
  • ¿Estamos creando una librería cuyo uso pueda caer en algún escenario de los anteriores?

Optimizar un código tiene un coste de desarrollo extra y muchas veces no vale la pena el esfuerzo necesario para conseguirlo. No es lo mismo optimizar un método que se ejecuta una vez al arrancar nuestra aplicación, por ejemplo, que un método que se llama cientos de veces por segundo. Si estamos ejecutando nuestra aplicación en un servidor tampoco es lo mismo que, si por el contrario la estamos ejecutando en una raspberry pi por ejemplo, los recursos en un caso son ‘ilimitados’ y en el otro no. A pesar de lo anterior… ¿Estamos teniendo problemas? Podemos tener un código que se ejecuta cientos de veces por segundo en un equipo con pocos recursos, pero aun, así no existir problemas en el rendimiento.

Si por el contrario eres de los que sí necesita optimizar tu código, agárrate que vienen curvas (aunque voy a intentar simplificarlo al máximo).

¿Cómo puedo saber dónde mejorar el rendimiento de mis aplicaciones .Net Core?

La respuesta a esta pregunta es muy variada y depende de que es exactamente lo que haga el código que queremos optimizar. Por ejemplo, ¿cúal de los siguientes métodos es mejor para concatenar dos cadenas?

public string StringConcat() => string.Concat(_stringA, _stringB);

public string StringFormat() => string.Format("{0}{1}", _stringA, _stringB);

public string StringInterpolation() => $"{_stringA}{_stringB}";

public string StringAdd() => _stringA + _stringB;

public string StringBuilder()
{
    var builder = new StringBuilder();
    builder.Append(_stringA);
    builder.Append(_stringB);
    return builder.ToString();
}

Siempre hemos oído que hacer una operación ‘+’ entre dos cadenas es lo peor que podemos hacer ya que las cadenas son inmutables y es una mala práctica, que es mejor utilizar un StringBuilder pero… ¿Serías capaz de decir de las 5 opciones cual es mejor y cual peor sin ser meras suposiciones?

Para poder optimizar cualquier parte del código, lo primero es medirlo. Si no tomamos métricas sobre los recursos que está utilizando un código, todo lo que hagamos serán meras suposiciones. Precisamente para temas como este, hace unos meses hablábamos sobre como medir el rendimiento de nuestro código utilizando BenchmarkDotNet y hoy vamos a recuperar la utilidad que ofrece para hacer precisamente eso, medir. No es posible optimizar código sin hacer diferentes mediciones que nos digan si estamos haciendo que las cosas vayan mejor o peor. Por ejemplo, sin medir nada, podemos pensar que lo mejor tal vez sea concatenar (el método StringConcat) o tal vez usar un StringBuilder, y que el peor quizás sea hacer una simple suma (el método StringAdd). Cuando hacemos un benchmark de esos cinco métodos, los resultados tal vez sorprendan a más de uno…

La imagen muestra los resultados para el código anterior donde los resultados dicen que StringConcat tarda 23 ns, StringFormat 118 ns, StringInterpolation 24 ns, StringAdd 23 ns y StringBuilder 49 ns,

Sin ánimo de venir a decir ahora que hacer suma de cadenas ya no es un problema (que sí lo es), lo que pretendo mostrar es que siempre es necesario medir para poder determinar dónde está el problema. En el caso anterior al ser solo dos cadenas y una única repetición, el caso de sumar cadenas no tiene impacto mientras que otros métodos pueden costar hasta 5 veces más, si estuviésemos haciendo esto en ciclos largos (por ejemplo 1000 veces) donde se haga la cadena A unida a la B y el resultado a la B evidentemente los resultados serían muy distintos:

La imagen muestra los resultados para el código anterior repetido 1000 veces donde los resultados dicen que StringConcat tarda 648 ns, StringFormat 1922 ns, StringInterpolation 616 ns, StringAdd 610 ns y StringBuilder 11 ns,

En este caso, solo nos hemos fijado en el tiempo de ejecución, pero… ¿Y la memoria? ¿Puede una mala gestión de memoria ser un problema que nos haga perder rendimiento en una aplicación .Net Core? La verdad es que sí. Por cómo trabaja .Net en general, la memoria la maneja una herramienta conocida como el Garbage Collector.

¿Cómo funciona el Garbage Collector en .Net Core?

El recolector de basura es una herramienta que utilizan lenguajes como C# o Java para gestionar la memoria de la aplicación. Básicamente el recolector se encarga de hacer las reservas de memoria cuando creamos un nuevo objeto por referencia. El concepto de la gestión de memoria en .Net es un tema complejo y muy interesante y saber sobre él nos permite generar código con mucho mejor rendimiento.

La memoria se divide en dos secciones diferentes con diferentes finalidades.

  • Stack (Pila)
  • Heap (Montón)

La pila es el lugar donde se almacenan las variables por valor (struct) mientras que el montón es donde se almacenan las variables por referencia (class).

Esto no es siempre así como plantearemos más adelante pero de momento podemos asumir que sí.

La pila es ‘reducida’ pero muy rápida y no es manejada por el recolector de basura mientras que el montón es más grande y si lo gestiona el recolector de basura y tiene una manera muy especial de hacerlo. De cara a optimizar el proceso de limpieza, .Net divide los objetos del montón en 3 generaciones, siendo la generación 0 la de los objetos más efímeros y la generación 2 la de los objetos más longevos de la aplicación.

El recolector de basura es quien se encarga de asignar la memoria cuando creamos nuevos objetos por referencia, pero no siempre tiene esa memoria disponible. Cuando no tiene suficiente memoria libre para darnos la que necesitamos, iniciará una recolección de la generación 0 que consiste en limpiar los objetos que ya no use usan y subir de generación los que sí se usan.

Después de hacer este trabajo, pueden pasar dos cosas, o ya hay memoria suficiente o no la hay. En caso de que la haya, nuestra aplicación sigue funcionando normalmente pero si no había memoria suficiente, lo que va a hacer el recolector es el mismo proceso con la generación 1. Por último, si la generación 1 tampoco es suficiente, hará lo mismo con la 2. La diferencia es que en caso de la generación 2, los objetos siguen en la generación 2.

Vale, llegados a este punto me vas a decir: `¡¡Menuda brasa!! ¿Y esto de qué me vale?`(Con razón avise de que venían curvas). Es importante saber cómo funciona el garbage collector porque, aunque su existencia nos facilita mucho la vida, su trabajo es más prioritario que el nuestro. Esto quiere decir que cada vez que el recolector hace una recolección nuestra aplicación se va a parar mientras dure la recolección. (Por suerte esto tampoco es así al 100% pero asumamos que sí).

Cuanto más trabaje el recolector de basura, peor rendimiento tendrá nuestra aplicación. En las mediciones del código que hemos hecho antes, solo estábamos mostrando el tiempo de ejecución, pero vamos a hacer lo mismo añadiendo la memoria:

La imagen muestra los resultados con memoria para el código anterior donde los resultados dicen que StringConcat consume, 4921 KB StringFormat 14899 KB, StringInterpolation 492 1KB, StringAdd 4921 KB y StringBuilder solo 26 KB

¿Cómo reduzco el consumo de memoria de mi aplicación?

En este punto tenemos claro que utilizar memoria de cualquier manera es algo que puede afectar negativamente al rendimiento y nosotros lo que queremos es precisamente mejorar el rendimiento de una aplicación .Net Core. Para eso .Net Core nos ofrece ciertas herramientas para poder hacer código ‘memory friendly’.

Para esto tenemos varias opciones built-in que nos van a permitir mejorar y optimizar las reservas de memoria, consiguiendo así ponerle las cosas más fáciles al recolector.

Devolver valores por referencia en vez de nuevas copias para mejorar el rendimiento de aplicaciones .Net Core

Acabamos de plantear que el hecho de instanciar clases tiene un coste para el recolector de basura, pero utilizar instancias de structs tiene también su personalización. Esto es porque al ser objetos por valor, cada vez que pasemos el dato como parámetro o retorno de un método, realmente estaremos pasando una copia completa de la estructura… En este caso no son los problemas con el recolector, sino la sobrecarga de copiar datos dentro de la pila lo que pretendemos mejorar. Imagina una estructura con varios campos, el hecho de que tengamos un método que retorne una copia o una referencia puede cambiar significativamente el tiempo cuando es un código ‘caliente’ por el que se pasa miles de veces. Los resultados de llamar a este código 1000 veces ya tienen una diferencia del 30%.

private BigStruct CreateBigStruct()
{
    return _bigStruct;
}

private ref BigStruct CreateRefBigStruct()
{
    return ref _bigStruct;
}
La imagen muestra los resultados de devolver una estructura por valor o por referencia, donde el hecho de devolverla por referencia mejora un 31% el tiempo usando .Net Core

Para poder conseguir esto, lo que tenemos que hacer es hacer es añadir el ‘ref’ a la firma de nuestro método. Tras esto, después del ‘return’ también añadimos ‘ref’, para indicar que el retorno es por valor. A la hora de consumir el retorno de este método utilizaremos variables ‘ref’ locales:

ref var item = ref CreateRefBigStruct();

Vale, pero con esto estamos devolviendo una referencia y podemos modificar el contenido de la estructura… ¿Esto se puede evitar de alguna manera? Pues la respuesta es sí, gracias a las herramientas del lenguaje disponibles podemos declarar el método como ‘readonly‘. De este modo conseguimos crear una referencia de solo lectura hacia la estructura, de modo que conseguimos lo mejor de los dos mundos.

private ref readonly BigStruct CreateRefBigStruct()
{
    return ref _bigStruct;
}
ref readonly var item = ref CreateRefBigStruct();

Span<T>/ReadOnlySpan<T>

La primera de estas herramientas es ‘ (opens in a new tab)» href=»https://docs.microsoft.com/es-es/dotnet/api/system.span-1?view=netcore-3.1″ target=»_blank»>Span<T>‘ y su versión de solo lectura ‘ (opens in a new tab)» href=»https://docs.microsoft.com/es-es/dotnet/api/system.readonlyspan-1?view=netcore-3.1″ target=»_blank»>ReadOnlySpan<T>‘. Gracias a ella vamos a generar una referencia hacia una colección en la memoria contigua que nos va a permitir hacer operaciones sobre ella. Todo esto sin tener que generar nuevas colecciones gracias a su método Slice. Esta memoria a la que apuntamos puede estar tanto en la pila como en el montón o incluso memoria no administrada. Imaginemos esto como una ventana móvil que apunta hacia una colección completa, cuando necesitemos acceder a una parte especifica de la colección, en vez de crear una subcolección, simplemente vamos a mover la ventana a la parte que nos interesa sin crear una colección nueva.

La imagen enseña mediante un diagrama que respecto a una colección completa, Span<T> permite abrir o cerrar la región que esta controlando mejorando así el rendimiento en .Net Core

‘Span<T>’ es un tipo de dato especial (ref struct) que nos garantiza que siempre se va a almacenar en la pila, por lo que solo podemos hacer uso de ella en situaciones que garanticen que esto se cumple. En caso contrario el compilador nos generará un error. Vamos a poder utilizar Span<T> como variable local, como parámetro o como retorno de un método, pero no como miembro de una clase o estructura.

Esto es porque, aunque las estructuras en principio van a la pila, no siempre tiene porque ser así. Si por ejemplo está dentro de una clase, la estructura se almacenará en el montón.

¿Y qué ventajas me da esto? Pues, por ejemplo, vamos a poder crear colecciones de datos en la pila mientras que si utilizásemos una lista o array, estarían en el montón. Para poder hacer esto vamos a utilizar la palabra reservada ‘stackalloc’ para indicar que esa memoria que queremos tiene que venir de la pila.

//Con reserva de memoria
int[] array = new int[1024];
//Sin reserva de memoria
Span<int> array = stackalloc int[1024];

Por poner un ejemplo, crear una colección de 100000 elementos y asignarles un valor:

public void InitializeWithArray()
{
    int[] array = new int[_lenght];
    for (var i = 0; i > _lenght; i++)
    {
        array[i] = i;
    }
}

public void InitializeWithStackalloc()
{
    Span<int> array = stackalloc int[_lenght];
    for (var i = 0; i > _lenght; i++)
    {
        array[i] = i;
    }
}

Nos aporta unos resultados muy claros:

La imagen muestra que utilizar Span consigue una reducción del tiempo de un 27% además de no consumir nada de memoria mientras que el método con array consume un total de 40 KB con lo que mejora el rendimiento en .Net Core

El hecho de utilizar Span<T> está suponiendo un ahorro del 27% en cuanto a ejecución y además estamos evitando el uso de 40 KB de memoria del montón.

La principal ventaja es que desde .Net Core 2.1 tiene un soporte nativo en el framework, por lo que ahora en .Net Core 3 son muchísimas las APIs del framework que nos da la opción de darle como parámetro o recibir como retorno objetos de tipo Span<T>, por lo que con unos cambios mínimos en nuestra manera de desarrollar podríamos aplicar una mejora en el rendimiento.

Memory<T>/ReadOnlyMemory<T>

Span<T> es muy útil como hemos visto a la hora de optimizar el uso de memoria y con ello el rendimiento en .Net Core, pero tiene sus limitaciones respecto a donde usarlo… Precisamente para suplir esas limitaciones está a nuestra disposición ‘ (opens in a new tab)» href=»https://docs.microsoft.com/es-es/dotnet/api/system.memory-1?view=netcore-3.1″ target=»_blank»>Memory‘ y su versión de solo lectura ‘ReadOnlyMemory‘. A diferencia de Span y ReadOnlySpan, estas dos nuevas versiones si pueden almacenarse en el montón y por tanto podemos usarlo en cualquier sitio.

ArrayPool<T>

Hasta ahora hemos planteado opciones para evitar el uso del montón para minimizar las recolecciones y evitar así tiempos muertos. Esto no siempre es posible porque la pila tiene un tamaño limitado. Si pretendemos almacenar grandes colecciones con estructuras en ella vamos a tener un desbordamiento…

Para evitar este problema podemos aplicar una estrategia de reciclaje de memoria gracias a ‘ (opens in a new tab)» href=»https://docs.microsoft.com/es-es/dotnet/api/system.buffers.arraypool-1?view=netcore-3.1″ target=»_blank»>ArrayPool‘. Cuando una aplicación arranca, automáticamente reserva una cierta cantidad de memoria en el montón que tiene a la espera para cuando se necesite. Precisamente gracias a ArrayPool vamos a pedir prestada esa memoria del montón para hacer las operaciones y después se la devolveremos. Empleando esta estrategia vamos a conseguir que no se necesiten hacer recolecciones sobre esa memoria y vamos a mejorar el rendimiento de aplicaciones .Net Core.

Para poder conseguir este objetivo, ArrayPool nos ofrece dos métodos con los que vamos a poder pedir y devolver memoria. Estos métodos son ‘Rent‘ y ‘Return‘.

var array = ArrayPool<BigStruct>.Shared.Rent(1024);
//Código que utiliza el array
ArrayPool<BigStruct>.Shared.Return(array);

Utilizando ArrayPool estamos pidiendo memoria al proceso y si no se la devolvemos, vamos a provocar una fuga de memoria que acabará en un fallo catastrófico.

Supongamos un caso donde tengamos una colección de 1000 posiciones de una estructura grande. La diferencia entre crear un array de la manera normal o utilizar ArrayPool es esta:

La imagen muestra que utilizar ArrayPool es más de 100 veces más eficiente además de no consumir nada de memoria

Como se puede comprobar, utilizar ArrayPool en torno a 100 veces más eficiente. Además de la mejora de la eficiencia, también hay un ahorro de la memoria utilizada por el proceso.

Conclusiones

Existen más métodos de optimización para mejorar el rendimiento en aplicaciones .Net Core con lo que conseguir mejoras muy importantes aunque en esta entrada he querido plantear los más generalistas. Dicho lo anterior vuelvo a reiterar que todo esto solo se debe utilizar si se necesita y solo si se necesita. El hecho de utilizar más structs y no pensar que las clases son la solución a todo es una buena idea, al igual que utilizar las APIs con Span siempre que se pueda, pero no volverse loco sin motivo. Optimizar código suele traer consigo el dificultar la lectura respecto a código sin optimizar. Por supuesto, antes de plantearse hacer cambios como estos, siempre hay que medir. La optimización prematura es una de las mayores fuentes de problemas a la hora de desarrollar.

¿Conocías estas maneras de mejorar el rendimiento para .Net Core? Si crees que me he dejado algún método importante no dudes en dejarlo en los comentarios y amplio esta entrada. Todas las herramientas disponibles en el cinturón siempre son buenas. 🙂

Para que cada uno pueda tomar sus propias conclusiones, he dejado el código en GitHub listo para poder ejecutarlo.

**La entrada Escribiendo código de alto rendimiento en .Net Core se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Variable not found

Publicación self-contained y single-file en .NET Core

febrero 11, 2020 07:05

.NET CoreComo sabemos, para ejecutar cualquier tipo de aplicación .NET Core en un equipo necesitamos tener instalado el runtime o el SDK de la plataforma. Esto es algo que podemos hacer muy fácilmente, simplemente acudiendo a la página oficial de descargas, eligiendo nuestro sistema operativo y siguiendo las instrucciones de instalación.

El hecho de que en el equipo destino esté preinstalado el runtime es muy interesante, entre otras cosas porque permite asegurar de antemano que en él se encontrarán todas las dependencias (frameworks, bibliotecas, paquetes, metapaquetes) necesarios para una correcta ejecución. Por tanto, para distribuir nuestra aplicación sólo debemos generar lo relativo a nuestro código, el resto ya estará allí.
Esta forma de publicar aplicaciones se denomina framework-dependent, pues dependen de que determinados componentes del framework estén instalado en el destino.
Por ejemplo, el paquete de publicación de una aplicación de consola prácticamente vacía, que únicamente muestra el mensaje "Hello world!", ocuparía solo 175K:
D:\MyConsoleApp\output>dir

El volumen de la unidad D es Datos
El número de serie del volumen es: 8CBC-81E3

Directorio de D:\MyConsoleApp\output

09/02/2020 18:47 <DIR> .
09/02/2020 18:47 <DIR> ..
09/02/2020 18:46 428 MyConsoleApp.deps.json
09/02/2020 18:46 4.608 MyConsoleApp.dll
09/02/2020 18:46 169.984 MyConsoleApp.exe
09/02/2020 18:46 668 MyConsoleApp.pdb
09/02/2020 18:46 154 MyConsoleApp.runtimeconfig.json
5 archivos 175.842 bytes
2 dirs 463.058.874.368 bytes libres

D:\MyConsoleApp\output>_
Otra ventaja de este tipo de distribución es que es cross platform pura, es decir, podemos copiar los archivos a cualquiera de los sistemas operativos soportados y, siempre que dispongan del runtime, nuestra aplicación podrá correr sobre ellos sin problema.

Y todo esto está muy bien, pero, ¿qué pasa si quiero crear una aplicación portable, de forma que pueda distribuirla y ejecutarla sin necesidad de que el equipo destino tenga nada preinstalado?

Pues eso es lo que veremos en este post ;)

Publicación self-contained

Este tipo de publicación incluye todo lo necesario para que la aplicación funcione, sin necesidad de que el equipo destino disponga de componentes preinstalados. Es la opción más segura si desconocemos qué runtimes están instalados en el servidor, o incluso si queremos aislarnos de cambios o actualizaciones que pudieran instalarse en el servidor y que, de alguna forma, pudieran salpicarnos en el futuro.

Para publicar en modo self contained desde Visual Studio, sólo tendremos que acudir al perfil de publicación y editar sus settings de la siguiente forma:

Perfil de publicación estableciendo el deployment mode a self-contained y el target runtime a win-x64

Como se observa en la captura anterior, en el Deployment mode debemos seleccionar "Self-contained", mientras que en la opción Target Runtime tendremos que indicar el runtime sobre el que vamos a ejecutar la aplicación. Este paso es importante, pues hará que los binarios a incluir en el paquete distribuible sean los específicos para el entorno indicado.

Esto también podemos hacerlo si queremos generar el paquete de publicación desde la línea de comandos, por ejemplo como sigue:
D:\MyConsoleApp>dotnet publish --self-contained -r win-x64 -c release -o output

Microsoft (R) Build Engine versión 16.4.0+e901037fe para .NET Core
Copyright (C) Microsoft Corporation. Todos los derechos reservados.

Restauración realizada en 138,79 ms para D:\MyConsoleApp\MyConsoleApp.csproj.
MyConsoleApp -> D:\MyConsoleApp\bin\release\netcoreapp3.1\win-x64\MyConsoleApp.dll
MyConsoleApp -> D:\MyConsoleApp\output\

D:\MyConsoleApp\output>_
En cualquier caso, el principal inconveniente de este modelo de distribución es que los archivos a mover son bastantes más, por lo que los despliegues serán más lentos y ocuparán mucho más espacio en disco. Por ejemplo, si hacemos un dir en la carpeta de resultados de la publicación veremos que hemos pasado de tener cinco archivos a más de doscientos, y el tamaño de menos de 200Kb a 69Mb:
D:\MyConsoleApp\output>dir

El volumen de la unidad D es Datos
El número de serie del volumen es: 8CBC-81E3

Directorio de D:\MyConsoleApp\output

09/02/2020 18:56 <DIR> .
09/02/2020 18:56 <DIR> ..
20/04/2018 06:28 19.208 api-ms-win-core-console-l1-1-0.dll
20/04/2018 06:28 18.696 api-ms-win-core-datetime-l1-1-0.dll
20/04/2018 06:28 18.696 api-ms-win-core-debug-l1-1-0.dll
20/04/2018 06:28 18.696 api-ms-win-core-errorhandling-l1-1-0.dll
20/04/2018 06:29 22.280 api-ms-win-core-file-l1-1-0.dll
20/04/2018 06:37 18.696 api-ms-win-core-file-l1-2-0.dll
(... Omitidos más de 200 archivos ...)
09/12/2019 03:39 14.928 System.Xml.XmlDocument.dll
09/12/2019 03:40 16.768 System.Xml.XmlSerializer.dll
09/12/2019 03:40 14.200 System.Xml.XPath.dll
09/12/2019 03:39 15.952 System.Xml.XPath.XDocument.dll
20/04/2018 06:37 1.016.584 ucrtbase.dll
09/12/2019 03:40 15.440 WindowsBase.dll
225 archivos 69.122.606 bytes
2 dirs 462.918.475.776 bytes libres

D:\MyConsoleApp\output>_
Sí, pensaréis que es un exceso para mostrar un simple "Hola mundo", pero si tenemos en cuenta que estos archivos incluyen el framework y todas sus bibliotecas no sale tan mal parada la cosa. Lo importante en este caso es que podemos copiar esta carpeta a cualquier servidor Windows x64 y funcionará correctamente, sin necesidad de tener nada preinstalado.

Pero aún podemos mejorarlo un poco...

Eliminando peso: Trimming de paquetes

Cuando hemos echado un ojo al contenido de la carpeta de destino de la publicación, vimos archivos como System.Xml.XmlSerializer.dll o System.Xml.XPath.dll... ¿realmente necesitamos desplegar estos ensamblados en nuestra aplicación, que sólo muestra un "Hello world!"? Seguro que no.

Desde .NET Core 3.0, el SDK incluye de serie una funcionalidad que permite eliminar de los archivos distribuibles los paquetes que no sean utilizados por nuestra aplicación o sus dependencias. Para activarlo, basta con añadir el elemento <PublishTrimmed> en el archivo .csproj del proyecto:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
Hecho esto, si volvemos a repetir la operación de publicación, veremos que el paquete de publicación se reducirá bastante:
D:\MyConsoleApp>dotnet publish --self-contained -r win-x64 -c release -o output

Microsoft (R) Build Engine versión 16.4.0+e901037fe para .NET Core
Copyright (C) Microsoft Corporation. Todos los derechos reservados.

Restauración realizada en 26,72 ms para D:\MyConsoleApp\MyConsoleApp.csproj.
MyConsoleApp -> D:\MyConsoleApp\bin\Release\netcoreapp3.1\win-x64\MyConsoleApp.dll
Se está optimizando el tamaño de los ensamblados, lo que puede cambiar el
comportamiento de la aplicación. Asegúrese de probarlo después de publicar.
Consulte https://aka.ms/dotnet-illink
MyConsoleApp -> D:\MyConsoleApp\output\

D:\MyConsoleApp>dir output

El volumen de la unidad D es Datos
El número de serie del volumen es: 8CBC-81E3

Directorio de D:\MyConsoleApp\output
09/02/2020 19:05 <DIR> .
09/02/2020 19:05 <DIR> ..
20/04/2018 06:28 19.208 api-ms-win-core-console-l1-1-0.dll
20/04/2018 06:28 18.696 api-ms-win-core-datetime-l1-1-0.dll
20/04/2018 06:28 18.696 api-ms-win-core-debug-l1-1-0.dll
20/04/2018 06:28 18.696 api-ms-win-core-errorhandling-l1-1-0.dll
(... Omitidos más de 50 archivos ...)
09/02/2020 19:05 62.464 System.Console.dll
07/12/2019 16:40 9.555.840 System.Private.CoreLib.dll
09/02/2020 19:05 74.752 System.Runtime.Extensions.dll
20/04/2018 06:37 1.016.584 ucrtbase.dll
63 archivos 26.499.046 bytes
2 dirs 462.951.739.392 bytes libres

D:\MyConsoleApp>_
Mucho mejor ahora: hemos pasado de 225 archivos a 63, y reducido el peso de 69 a 26Mb. Aunque sigue siendo demasiado para un simple "Hello world!", al menos sabemos que es el mínimo al que podemos aspirar si queremos que el paquete distribuible de nuestra aplicación incluya el framework sobre el que será ejecutada.

Un último detalle: como hemos comentado anteriormente, el trimming eliminará los paquetes que detecte que no son utilizados, pero puede haber ocasiones en las que nos interesa que este mecanismo no elimine algún ensamblado en particular. Por ejemplo, si nuestra aplicación utiliza reflexión o cualquier otro mecanismo para cargar o utilizar ensamblados, el trimmer no los detectará y asumirá que no se están usando, lo que provocará errores en tiempo de ejecución.

Para indicar que un ensamblado debe ser incluido obligatoriamente en el paquete de publicación, podemos usar el elemento <TrimmerRootAssembly> en el .csproj:
<ItemGroup>
<TrimmerRootAssembly Include="System.Xml.XmlSerializer.dll" />
</ItemGroup>

Publicación single-file: ¡un único ejecutable!

A partir de .NET Core 3, tenemos disponible un nuevo modelo de distribución que permite incluir en un único ejecutable todo lo necesario para que nuestra aplicación funcione sin tener nada preinstalado.

Podemos publicar como archivo único mediante una orden de la CLI como la siguiente:
D:\MyConsoleApp>dotnet publish -r win10-x64 -p:PublishSingleFile=true -o output

Microsoft (R) Build Engine versión 16.4.0+e901037fe para .NET Core
Copyright (C) Microsoft Corporation. Todos los derechos reservados.

Restauración realizada en 124,52 ms para D:\MyConsoleApp\MyConsoleApp.csproj.
MyConsoleApp -> D:\MyConsoleApp\bin\Debug\netcoreapp3.1\win10-x64\MyConsoleApp.dll
Se está optimizando el tamaño de los ensamblados, lo que puede cambiar el
comportamiento de la aplicación. Asegúrese de probarlo después de publicar.
Consulte https://aka.ms/dotnet-illink
MyConsoleApp -> D:\MyConsoleApp\output\

D:\MyConsoleApp>dir output

El volumen de la unidad D es Datos
El número de serie del volumen es: 8CBC-81E3

Directorio de D:\MyConsoleApp\output

09/02/2020 19:10 <DIR> .
09/02/2020 19:10 <DIR> ..
09/02/2020 19:10 26.501.267 MyConsoleApp.exe
09/02/2020 19:10 680 MyConsoleApp.pdb
2 archivos 26.501.947 bytes
2 dirs 462.872.199.168 bytes libres

D:\MyConsoleApp>_
En lugar de tener que indicar tantos parámetros cada vez, también podríamos conseguirlo modificando el archivo .csproj. Para ello, bastaría con establecer a true el elemento <PublishSingleFile>, aunque al hacerlo, además, será obligatorio introducir el runtime de destino en el item <RuntimeIdentifier>. El resultado final podría ser algo así (trimming incluido):
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<PublishSingleFile>true</PublishSingleFile>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
En cualquier caso, fijaos que podríamos distribuir la aplicación, el runtime y el framework en un único ejecutable de 26Mb (usando el trimmer).

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 390

febrero 10, 2020 07:27

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

    Web / HTML / CSS / Javascript

    Visual Studio / Complementos / Herramientas

    Xamarin / Mobile

    Otros

    Publicado en Variable not found.

    » Leer más, comentarios, etc...

    Blog Bitix

    Tareas básicas después de instalar una distribución GNU/Linux

    febrero 09, 2020 09:00

    Para utilizar de forma efectiva y eficiente una distribución GNU/Linux es necesario conocer las tareas básicas que hay que realizar en todo sistema. Estas son actualizar los paquetes instalados del sistema a nuevas versiones con correcciones de seguridad, correcciones de errores y mejoras, instalar y desinstalar nuevos paquetes y programas. Conocer el uso básico de la terminal permite automatizar y realizar de forma masiva algunas tareas además de también permitir actualizar el sistema e instalar y desinstalar programas.

    GNU

    Linux

    Si has instalado recientemente o piensas instalar una distribución GNU/Linux después de elegir la distribución GNU/Linux que más se adapte a tus preferecias y de seguir los pasos para instalar una como Ubuntu, después es necesario conocer unas pocas tareas de administración del sistema. En cada distribución varía ligeramente pero en todas hay que realizar unas tareas básicas de mantenimiento.

    Estas tareas básicas de mantenimiento son:

    • Actualizar los paquetes instalados del sistema. Los paquetes actualizados incluyen correcciones de seguridad por lo que es importante actualizar el sistema de forma regular. También, pueden incluir nuevas versiones de los paquetes con nuevas funcionalidades y correcciones de errores. Una programa que es necesario mantener actualizado es el navegador web, también el núcleo o kernel de Linux.
    • Instalar y desinstalar nuevos paquetes y programas. Dependiendo de las tareas que se deseen realizar hay que instalar los programas que permitan realizarlas. Para editar documentos ofimáticos, un navegador web, retocar imágenes, correo electrónico, descarga de torrents, reproductor de vídeo, reproductor de música, captura de imágenes, captura vídeo del escritorio, programas para el desarrollo y programación, virtualización, … Cada programa tienen su paquete en la ditribución que es necesario instalar para usarlo y desinstalar cuando el programa ya no se va a usar más. Es dificil que no encuentres un programa que realice lo que se desea.
    • Uso básico de la terminal. Hay programas con interfaz gráfica pero para algunas tareas es más rápido hacerlas desde la línea de comandos con la ventaja que con un script es posible automatizar en caso de ser repetitiva. Desde la línea de comandos hay numerosos programas útiles que además se pueden combinar de forma que la salida de uno sea la entrada de otro.

    Dependiendo de la distribución cada una de estas tareas puede variar el comando en concreto pero en general en todas se realizan de forma similar. A continuación comento como realizar las tareas en dos de las distribuciones más populares como son Ubuntu y Arch Linux pero en Fedora, Debian, elementary OS se realizan de forma similar.

    Ubuntu Arch Linux

    Actualizar los paquetes instalados del sistema

    En todas las distribuciones hay un gestor de paquetes que se encarga de forma automatizada de descargar desde los repositorios las nuevas versiones y actualizar los paquetes. Cada paquete tiene unas dependencias que el gestor de paquetes también se encarga de descargar, instalar y actualizar. Es importante realizar la actualización regularmente, todas las semanas o cada dos semanas, dado que estos incluyen importantes correcciones de seguridad, correcciones de errores o mejoras con nuevas opciones.

    En Ubuntu el gestor de paquetes es apt, la actualización de los paquetes instalados a la última versión disponible en los repositorios con las correcciones de seguridad y de errores se realiza con el siguiente comando. Arch Linux también tiene su comando para realizar la actualización de todos los paquetes del sistema.

    1
    2
    
    $ sudo apt update
    $ sudo apt upgrade
    actualizar-sistema-ubuntu.sh

    Dado que Ubuntu no es una distribución rolling release sino que tiene un calendario de publicación basado en fechas planificadas cada 6 meses y de dos años para las versiones de soporte largo o LTS cuando se lanza una nueva versión de la distribución hay que actualizar la versión del sistema. Se puede instalar el sistema completo desde cero en la nueva versión o actualizar la versión instalada en el sistema a la nueva versión. En ambos casos es recomendable previamente realizar una copia de seguridad por si en el proceso se produce algún tipo de error inesperado en el raro caso de que el sistema no llegue ni siquiera a entrar al entorno de escritorio.

    1
    2
    3
    
    $ sudo apt update
    $ sudo apt upgrade
    $ sudo apt full-upgrade
    actualizar-version-ubuntu.sh

    En Arch Linux la actualización de los paquetes se realiza con el el siguiente comando. Dado que Arch Linux es una distribución rolling release en la que en todo momento se disponen de las últimas versiones de los paquetes y programas no hay que hacer actualizaciones a nuevas versiones de la distribución sino que esta se mantiene en constante actualización. Lo importante en Arch Linux es hacer siempre actualizaciones completas del sistema y no parciales o de un programa individualmente dado que en algún caso es posible que la versiones de los paquetes de diferentes versiones de diferentes paquetes sean incompatibles.

    1
    
    $ sudo pacman -Syu
    actualizar-sistema-archlinux.sh

    En el raro caso de que al actualizar un paquete en Arch Linux haya algún error se puede desactualizar a la versión anterior o hacer un downgrade.

    Instalar y desinstalar nuevos paquetes y programas

    Los programas y comandos permiten realizar las tareas de productividad que se deseen realizar. Para instalar nuevos programas también se utiliza el gestor de paquetes. Se puede realizar desde la linea de comandos o de forma gráfica usando el centro de software de GNOME. Basta con buscar el programa deseado y pulsar el botón instalar, la desinstalación se realiza tambíen desde el centro de Software de GNOME con el botón desinstalar.

    Centro de software de GNOME Programas en el centro de software

    Centro de software de GNOME

    Si en algún momento se deja de usar un programa o se reemplaza por otro que que se considere mejor es recomendable desinstalar el antiguo lo que permite recuperar el espacio en el almacenamiento persistente que ocupe y evitar la necesidad de descargar las actualizaciones de seguridad de un programa que no se usa.

    Si se trata de un programa gráfico al instalar el programa se añade un acceso directo en el lanzador de programas del entorno de escritorio como en GNOME, el entorno de escritorio KDE también tiene el suyo.

    Lanzador de aplicaciones de GNOME

    Lanzador de aplicaciones de GNOME

    Los programas se pueden usar inmediatamente después de completar su instalación sin necesidad de reiniciar el sistema, las actualizaciones de componentes clave del sistema como el kernel se instalan pero requieren un reinicio para que sean efectivas, este reinicio se puede realizar a conveniencia del usuario sin interrumpir de manera forzosa las tareas que esté realizando.

    Lanzador de aplicaciones de GNOME

    Reinicio del sistema después de una actualización de software

    Ubuntu tiene un proceso que se ejecuta periódicamente y notifica al usuario si hay nuevas actualizaciones en la distribución, si las hay muestra un diálogo para aplicarlas. En Arch Linux las actualizaciones se inician a petición del usuario.

    Actualizar Ubuntu Actualizar Ubuntu Actualizar Ubuntu

    Actualizar Ubuntu

    Desde la línea de comandos el gestor de paquetes también permite instalar y desinstalar programas, basta con conocer el nombre del paquete. La base de datos de paquetes de Ubuntu y de base de datos de Arch Linux permiten hacer búsquedas por nombre, en el caso de Ubuntu hay varias bases de datos una por versión de la distribución de modo que hay que buscar en la que se tenga instalada.

    1
    
    $ apt install libreoffice libreoffice-l10n-es
    instalar-programas-ubuntu.sh
    1
    
    $ sudo pacman -S libreoffice-fresh libreoffice-fresh-es
    instalar-programas-archlinux.sh

    El centro de software de GNOME instala los programas empaquetados con Flatpak en vez de usando los paquetes de la distribución.

    Uso básico de la terminal

    La línea de comandos de GNU/Linux al principio es difícil de utilizar dado que no es muy amigable al tener que conocer los comandos y sus parámetros para realizar la acción, las interfaces gráficas son más simples de utilizar dado que ofrecen una guía al usuario sin necesidad de que tenga un conocimiento previo de cómo utilizarlo.

    Sin embargo, para tareas repetitivas o masivas es mas rápido y sencillo utilizar la linea de comandos conociendo el comando y los parámetros a utilizar. Un ejemplo de tarea para que se puede utilizar la línea de comandos es para convertir de forma masiva el formato de imágenes, música o vídeos.

    1
    2
    3
    
    $ for f in *.wav; do ffmpeg -i "$f" -acodec libmp3lame "${f%.wav}.mp3"; done;
    $ for f in *.wav; do ffmpeg -i "$f" -acodec libvorbis "${f%.wav}.ogg"; done;
    $ for f in *.mp3; do ffmpeg -i "$f" -acodec libvorbis "${f%.mp3}.ogg"; done;
    audio-masive-convert.sh
    1
    
    $ for f in *.mkv; do ffmpeg -i "$f" -c:v mpeg2video -c:a libmp3lame -b:v 2500K -b:a 192K -vf scale=720x406,setdar=16:9 "${f%.mkv}.mpg"; done;
    video-masive-convert.sh
    1
    2
    
    $ for f in *.png; do convert -define webp:lossless=true "$f" "${f%.*}.webp"; done;
    $ for f in *.jpg; do convert -define webp:lossless=false "$f" "${f%.*}.webp"; done;
    convert-to-webp.sh

    Además, los comandos se pueden componer de modo que la salida de un comando sea la entrada de otro. En este ejemplo un archivo de texto con una serie de palabras si se desea ordenar de forma ascendente y eliminar las palabras duplicadas. El comando cat lee un archivo y lo emite en su salida, sort realiza la ordenación y uniq que elimina las líneas duplicadas. La salida de un programa se conecta a la entrada de otro usando una barra vertical, |.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    Lorem
    ipsum
    dolor
    sit
    amet
    consectetur
    adipiscing
    elit
    sed
    eiusmod
    tempor
    incidunt
    ut
    labore
    et
    dolore
    magna
    aliqua
    aliqua
    
    texto.txt
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    $ cat texto.txt | sort | uniq
    adipiscing
    aliqua
    amet
    consectetur
    dolor
    dolore
    eiusmod
    elit
    et
    incidunt
    ipsum
    labore
    Lorem
    magna
    sed
    sit
    tempor
    ut
    sort.sh

    La parte de GNU de GNU/Linux incluye una colección importante y numerosa de comandos algunos de estos son: cal, date, at, head, tail, vim, nano, sort, alias, grep, cd, chmod, chown, curl, diff, echo, find, history, kill, less, ls, man, mkdir, rmdir, mv, rm, cp, ping, pwd, ssh, sudo, tail, tar, gzip, top, iotp, uname, awk, xargs, unzip, crontab, systemd, mount, whatis, locate, uniq, seq, jq, traceroute. No es necesario conocerlos todos y sus opciones pero es muy util que existen y saber al menos que hacen, al necesitar usarlos basta con hacer una búsqueda en internet para evr un ejemplo de uso con sus opciones.

    Los programas de línea de comandos ofrecen páginas de manual para aprender qué hacen, cuáles son sus parámetros y cómo utilizarlos con el comando man.

    1
    2
    3
    
    $ man cat
    $ man sort
    $ man uniq
    man.sh

    Cada entorno de escritorio ofrece un programa gráfico de una terminal virtual.

    Terminal de GNOME

    Terminal de GNOME

    El intérprete de comandos es el encargado de entender las sintaxis de los comandos además de interpretar los scripts para automatizar las tareas con pequeños programas de script en vez de tener que introducir los comandos manualmente en la terminal. El intérprete de comandos Bash es un intérprete que es instalado por defecto en la mayoría de distribuciones GNU/Linux.

    Que no hacer

    Por muchas medidas de seguridad que implemente un sistema no son suficientes si el usuario no es consciente de algunos peligros y cosas que no se deben hacer sin saber que se está haciendo.

    Lo primero es no ejecutar cualquier comando que se encuentre en internet sin saber que hace por muy curioso que sea. Un ejemplo es el comando fork bomb, este hace que pasados unos pocos segundos el sistema agote todos sus recursos, lo hace inusable y obliga ha hacer un reinicio. Y este comando no es un comando de las peores maldades que se pueden hacer.

    :(){ :|:& };:

    Al igual que los comandos es conveniente no ejecutar programas que no provenga de una fuente de confianza, normalmente los repositorios de software de la distribución o el centro de software. En Windows es común ejecutar cracks y activadores para usar Windows, Microsoft Office u otros programas que requieren comprar una licencia de uso. Estos programas activadores es habitual que contengan virus y una fuente de infección del sistema con un peligro para la seguridad, ya solo acceder a las páginas de baja reputación desde la que descargarlos es un peligro.

    En GNU/Linux la mayor parte del software no tiene coste y está disponible en los repositorios de software, puede ser necesario descargar un programa que no se encuentre ahí pero en caso necesario hay que hacerlo siempre desde la página oficial y prestando especial atención si para ejecutarlo requiere concederle permisos de superusuario.

    » Leer más, comentarios, etc...

    Blog Bitix

    Tareas básicas de administración y uso después de instalar una distribución GNU/Linux

    febrero 09, 2020 09:00

    Para utilizar de forma efectiva y eficiente una distribución GNU/Linux es necesario conocer las tareas básicas que hay que realizar en todo sistema. Estas son actualizar los paquetes instalados del sistema a nuevas versiones con correcciones de seguridad, correcciones de errores y mejoras, instalar y desinstalar nuevos paquetes y programas. Conocer el uso básico de la terminal permite automatizar y realizar de forma masiva algunas tareas además de también permitir actualizar el sistema e instalar y desinstalar programas.

    GNU

    Linux

    Si has instalado recientemente o piensas instalar una distribución GNU/Linux después de elegir la distribución GNU/Linux que más se adapte a tus preferecias y de seguir los pasos para instalar una como Ubuntu, después es necesario conocer unas pocas tareas de administración del sistema, [las aplicaciones del entorno de escritorio de GNOME][blogbitix-464] y un listado de programas básicos según categoría en GNU/Linux. En cada distribución varía ligeramente pero en todas hay que realizar unas tareas básicas de mantenimiento.

    Estas tareas básicas de mantenimiento son:

    • Actualizar los paquetes instalados del sistema. Los paquetes actualizados incluyen correcciones de seguridad por lo que es importante actualizar el sistema de forma regular. También, pueden incluir nuevas versiones de los paquetes con nuevas funcionalidades y correcciones de errores. Una programa que es necesario mantener actualizado es el navegador web, también el núcleo o kernel de Linux.
    • Instalar y desinstalar nuevos paquetes y programas. Dependiendo de las tareas que se deseen realizar hay que instalar los programas que permitan realizarlas. Para editar documentos ofimáticos, un navegador web, retocar imágenes, correo electrónico, descarga de torrents, reproductor de vídeo, reproductor de música, captura de imágenes, captura vídeo del escritorio, programas para el desarrollo y programación, virtualización, … Cada programa tienen su paquete en la ditribución que es necesario instalar para usarlo y desinstalar cuando el programa ya no se va a usar más. Es dificil que no encuentres un programa que realice lo que se desea.
    • Uso básico de la terminal. Hay programas con interfaz gráfica pero para algunas tareas es más rápido hacerlas desde la línea de comandos con la ventaja que con un script es posible automatizar en caso de ser repetitiva. Desde la línea de comandos hay numerosos programas útiles que además se pueden combinar de forma que la salida de uno sea la entrada de otro.

    Dependiendo de la distribución cada una de estas tareas puede variar el comando en concreto pero en general en todas se realizan de forma similar. A continuación comento como realizar las tareas en dos de las distribuciones más populares como son Ubuntu y Arch Linux pero en Fedora, Debian, elementary OS se realizan de forma similar.

    Ubuntu Arch Linux

    Actualizar los paquetes instalados del sistema

    En todas las distribuciones hay un gestor de paquetes que se encarga de forma automatizada de descargar desde los repositorios las nuevas versiones y actualizar los paquetes. Cada paquete tiene unas dependencias que el gestor de paquetes también se encarga de descargar, instalar y actualizar. Es importante realizar la actualización regularmente, todas las semanas o cada dos semanas, dado que estos incluyen importantes correcciones de seguridad, correcciones de errores o mejoras con nuevas opciones.

    En Ubuntu el gestor de paquetes es apt, la actualización de los paquetes instalados a la última versión disponible en los repositorios con las correcciones de seguridad y de errores se realiza con el siguiente comando. Arch Linux también tiene su comando para realizar la actualización de todos los paquetes del sistema.

    1
    2
    
    $ sudo apt update
    $ sudo apt upgrade
    actualizar-sistema-ubuntu.sh

    Dado que Ubuntu no es una distribución rolling release sino que tiene un calendario de publicación basado en fechas planificadas cada 6 meses y de dos años para las versiones de soporte largo o LTS cuando se lanza una nueva versión de la distribución hay que actualizar la versión del sistema. Se puede instalar el sistema completo desde cero en la nueva versión o actualizar la versión instalada en el sistema a la nueva versión. En ambos casos es recomendable previamente realizar una copia de seguridad por si en el proceso se produce algún tipo de error inesperado en el raro caso de que el sistema no llegue ni siquiera a entrar al entorno de escritorio.

    1
    2
    3
    
    $ sudo apt update
    $ sudo apt upgrade
    $ sudo apt full-upgrade
    actualizar-version-ubuntu.sh

    En Arch Linux la actualización de los paquetes se realiza con el el siguiente comando. Dado que Arch Linux es una distribución rolling release en la que en todo momento se disponen de las últimas versiones de los paquetes y programas no hay que hacer actualizaciones a nuevas versiones de la distribución sino que esta se mantiene en constante actualización. Lo importante en Arch Linux es hacer siempre actualizaciones completas del sistema y no parciales o de un programa individualmente dado que en algún caso es posible que la versiones de los paquetes de diferentes versiones de diferentes paquetes sean incompatibles.

    1
    
    $ sudo pacman -Syu
    actualizar-sistema-archlinux.sh

    En el raro caso de que al actualizar un paquete en Arch Linux haya algún error se puede desactualizar a la versión anterior o hacer un downgrade.

    Instalar y desinstalar nuevos paquetes y programas

    Los programas y comandos permiten realizar las tareas de productividad que se deseen realizar. Para instalar nuevos programas también se utiliza el gestor de paquetes. Se puede realizar desde la linea de comandos o de forma gráfica usando el centro de software de GNOME. Basta con buscar el programa deseado y pulsar el botón instalar, la desinstalación se realiza tambíen desde el centro de Software de GNOME con el botón desinstalar.

    Centro de software de GNOME Programas en el centro de software

    Centro de software de GNOME

    Si en algún momento se deja de usar un programa o se reemplaza por otro que que se considere mejor es recomendable desinstalar el antiguo lo que permite recuperar el espacio en el almacenamiento persistente que ocupe y evitar la necesidad de descargar las actualizaciones de seguridad de un programa que no se usa.

    Si se trata de un programa gráfico al instalar el programa se añade un acceso directo en el lanzador de programas del entorno de escritorio como en GNOME, el entorno de escritorio KDE también tiene el suyo.

    Lanzador de aplicaciones de GNOME

    Lanzador de aplicaciones de GNOME

    Los programas se pueden usar inmediatamente después de completar su instalación sin necesidad de reiniciar el sistema, las actualizaciones de componentes clave del sistema como el kernel se instalan pero requieren un reinicio para que sean efectivas, este reinicio se puede realizar a conveniencia del usuario sin interrumpir de manera forzosa las tareas que esté realizando.

    Lanzador de aplicaciones de GNOME

    Reinicio del sistema después de una actualización de software

    Ubuntu tiene un proceso que se ejecuta periódicamente y notifica al usuario si hay nuevas actualizaciones en la distribución, si las hay muestra un diálogo para aplicarlas. En Arch Linux las actualizaciones se inician a petición del usuario.

    Actualizar Ubuntu Actualizar Ubuntu Actualizar Ubuntu

    Actualizar Ubuntu

    Desde la línea de comandos el gestor de paquetes también permite instalar y desinstalar programas, basta con conocer el nombre del paquete. La base de datos de paquetes de Ubuntu y de base de datos de Arch Linux permiten hacer búsquedas por nombre, en el caso de Ubuntu hay varias bases de datos una por versión de la distribución de modo que hay que buscar en la que se tenga instalada.

    1
    
    $ apt install libreoffice libreoffice-l10n-es
    instalar-programas-ubuntu.sh
    1
    
    $ sudo pacman -S libreoffice-fresh libreoffice-fresh-es
    instalar-programas-archlinux.sh

    El centro de software de GNOME instala los programas empaquetados con Flatpak en vez de usando los paquetes de la distribución.

    Uso básico de la terminal

    La línea de comandos de GNU/Linux al principio es difícil de utilizar dado que no es muy amigable al tener que conocer los comandos y sus parámetros para realizar la acción, las interfaces gráficas son más simples de utilizar dado que ofrecen una guía al usuario sin necesidad de que tenga un conocimiento previo de cómo utilizarlo.

    Sin embargo, para tareas repetitivas o masivas es mas rápido y sencillo utilizar la linea de comandos conociendo el comando y los parámetros a utilizar. Un ejemplo de tarea para que se puede utilizar la línea de comandos es para convertir de forma masiva el formato de imágenes, música o vídeos.

    1
    2
    3
    
    $ for f in *.wav; do ffmpeg -i "$f" -acodec libmp3lame "${f%.wav}.mp3"; done;
    $ for f in *.wav; do ffmpeg -i "$f" -acodec libvorbis "${f%.wav}.ogg"; done;
    $ for f in *.mp3; do ffmpeg -i "$f" -acodec libvorbis "${f%.mp3}.ogg"; done;
    audio-masive-convert.sh
    1
    
    $ for f in *.mkv; do ffmpeg -i "$f" -c:v mpeg2video -c:a libmp3lame -b:v 2500K -b:a 192K -vf scale=720x406,setdar=16:9 "${f%.mkv}.mpg"; done;
    video-masive-convert.sh
    1
    2
    
    $ for f in *.png; do convert -define webp:lossless=true "$f" "${f%.*}.webp"; done;
    $ for f in *.jpg; do convert -define webp:lossless=false "$f" "${f%.*}.webp"; done;
    convert-to-webp.sh

    Además, los comandos se pueden componer de modo que la salida de un comando sea la entrada de otro. En este ejemplo un archivo de texto con una serie de palabras si se desea ordenar de forma ascendente y eliminar las palabras duplicadas. El comando cat lee un archivo y lo emite en su salida, sort realiza la ordenación y uniq que elimina las líneas duplicadas. La salida de un programa se conecta a la entrada de otro usando una barra vertical, |.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    Lorem
    ipsum
    dolor
    sit
    amet
    consectetur
    adipiscing
    elit
    sed
    eiusmod
    tempor
    incidunt
    ut
    labore
    et
    dolore
    magna
    aliqua
    aliqua
    
    texto.txt
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    $ cat texto.txt | sort | uniq
    adipiscing
    aliqua
    amet
    consectetur
    dolor
    dolore
    eiusmod
    elit
    et
    incidunt
    ipsum
    labore
    Lorem
    magna
    sed
    sit
    tempor
    ut
    sort.sh

    La parte de GNU de GNU/Linux incluye una colección importante y numerosa de comandos algunos de estos son: cal, date, at, head, tail, vim, nano, sort, alias, grep, cd, chmod, chown, curl, diff, echo, find, history, kill, less, ls, man, mkdir, rmdir, mv, rm, cp, ping, pwd, ssh, sudo, tail, tar, gzip, top, iotp, uname, awk, xargs, unzip, crontab, systemd, mount, whatis, locate, uniq, seq, jq, traceroute. No es necesario conocerlos todos y sus opciones pero es muy util que existen y saber al menos que hacen, al necesitar usarlos basta con hacer una búsqueda en internet para evr un ejemplo de uso con sus opciones.

    Los programas de línea de comandos ofrecen páginas de manual para aprender qué hacen, cuáles son sus parámetros y cómo utilizarlos con el comando man.

    1
    2
    3
    
    $ man cat
    $ man sort
    $ man uniq
    man.sh

    Cada entorno de escritorio ofrece un programa gráfico de una terminal virtual.

    Terminal de GNOME

    Terminal de GNOME

    El intérprete de comandos es el encargado de entender las sintaxis de los comandos además de interpretar los scripts para automatizar las tareas con pequeños programas de script en vez de tener que introducir los comandos manualmente en la terminal. El intérprete de comandos Bash es un intérprete que es instalado por defecto en la mayoría de distribuciones GNU/Linux.

    Que no hacer

    Por muchas medidas de seguridad que implemente un sistema no son suficientes si el usuario no es consciente de algunos peligros y cosas que no se deben hacer sin saber que se está haciendo.

    Lo primero es no ejecutar cualquier comando que se encuentre en internet sin saber que hace por muy curioso que sea. Un ejemplo es el comando fork bomb, este hace que pasados unos pocos segundos el sistema agote todos sus recursos, lo hace inusable y obliga ha hacer un reinicio. Y este comando no es un comando de las peores maldades que se pueden hacer.

    :(){ :|:& };:

    Al igual que los comandos es conveniente no ejecutar programas que no provenga de una fuente de confianza, normalmente los repositorios de software de la distribución o el centro de software. En Windows es común ejecutar cracks y activadores para usar Windows, Microsoft Office u otros programas que requieren comprar una licencia de uso. Estos programas activadores es habitual que contengan virus y una fuente de infección del sistema con un peligro para la seguridad, ya solo acceder a las páginas de baja reputación desde la que descargarlos es un peligro.

    En GNU/Linux la mayor parte del software no tiene coste y está disponible en los repositorios de software, puede ser necesario descargar un programa que no se encuentre ahí pero en caso necesario hay que hacerlo siempre desde la página oficial y prestando especial atención si para ejecutarlo requiere concederle permisos de superusuario.

    » Leer más, comentarios, etc...

    Blog Bitix

    Programación orientada a aspectos con AspectJ, Spring AOP y la clase Proxy

    febrero 07, 2020 07:30

    Los aspectos permiten separar código con distintas funcionalidades y centralizar un código común que sin utilizarlos está repartido por toda la aplicación. Son un concepto potente y una vez entendidos sus conceptos ofrecen muchas posibilidades para simplificar el código y mejorar su mantenimiento. Hay varias posibilidades, dos de las más utilizadas son AspectJ y Spring AOP, en el caso de que estas no se puedan utilizar el JDK incluye la clase Proxy para usos básicos aunque más limitados.

    Java

    Ciertas funcionalidades son transversales y están repartidas por toda la aplicación. Añadir y mezclar el código de esta funcionalidades con el código en los métodos hace que el código del método sea más complicado incluso puede que ese código de utilidad sea de mayor tamaño que el fundamental del método.

    Algunos ejemplos de funcionalidades transversales son trazas, métricas de rendimiento, seguridad, caches o transacciones. La programación orientada a aspectos permite extraer este código transversal y aplicarlo en aquellos puntos de la aplicación donde sea necesario sin estar mezclado con el código al que se aplica. Esto facilita la legibilidad del código, su mantenimiento y la separación con conceptos.

    La programación orientada a aspectos se usa mucho en las aplicaciones que usan Spring pero hay otras librerías que lo permiten, incluso el propio JDK tiene alguna clase sin necesitar de dependencias adicionales.

    La programación define varios términos:

    • Aspect: es una funcionalidad genérica aplicable a múltiples objetos. Cada aspecto trata una sola funcionalidad.
    • Join point: es el punto de ejecución donde se puede aplicar un aspecto como la llamada a un método, su retorno o el acceso a una propiedad.
    • Advice: es la acción que se realiza en un pointcut.
    • Pointcut: es una expresión que busca joint points, tiene un advice asociado que se ejecuta en todos los joint points que concuerdan con la expresión.
    • weaving: proceso que aplica los aspectos a las clases, puede ser en tiempo de compilación o en tiempo de ejecución.

    Esta es una clase normal con un método en la que a modo de ejemplo en la llamada al método se le apliquen dos aspectos, uno para añadir una traza cuando se llame al método y su valor de retorno y otro aspecto para medir cuando tiempo tarda en ejecutarse. La clase Foo descnoce los aspectos que se van a aplicar, no hay que hacer ninguna modificación en ella ni para añadirle los aspectos ni para quitarselos.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    package io.github.picodotdev.blogbitix.aspects;
    
    import java.util.Random;
    
    public class Foo implements IFoo {
    
        public void echo() {
            System.out.println("echo");
        }
    
        public int sum(int a, int b) {
            return a + b;
        }
    
        public void sleep() {
            try {
                long time = new Random().nextInt(1500);
                Thread.sleep(time);
            } catch(Exception e) {}
        }
    }
    Foo.java

    La interfaz solo es necesaria para un aspecto implementado con la clase Proxy de Java.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    package io.github.picodotdev.blogbitix.aspects;
    
    import java.util.Random;
    
    public interface IFoo {
    
        void echo();
        int sum(int a, int b);
        void sleep();
    }
    IFoo.java

    Se recomienda usra la forma más simple que sea suficiente para las necesidad de la aplicación. Spring AOP es más simple que usar AspectJ y no hay necesidad de aplicar el compilador de AspectJ en el proceso de compilación. Si solo se necesita aplicar advices en la ejecución de métodos de beans de Spring, Spring AOP es suficiente.

    Si no se usa spring o se necesitan aplicar aspectos en objetos no gestionados por el contenedor de Spring (como objetos de dominio) o aplicar advices en joint points distintos a las ejecuciones de métodos, por ejemplo para la obtención o asignación de una propiedad entonces la opción a usar es AspectJ.

    Programación orientada a aspectos con AspectJ

    AspectJ es una librería específica y la que más posibilidades ofrece de las que muestro en el artículo. Hay varias formas de utilizar AspectJ, la de usarla mediante anotaciones es bastante simple.

    Una de las ventajas de AspectJ es que no requiere usar Spring para utilizarla pero para ello en el momento de compilación hay que realizar un proceso denominado weaving para añadir la funcionalidad de los aspectos que transformar el bytecode de las clases. Aplicar los aspectos transformando el código permite que los aspectos no penalicen en tiempo de ejecución y ofrezca mejor rendimiento que Spring AOP, aunque el rendimiento no es algo determinante en la mayoría de los proyectos. Por contra es más compleja y requiere aplicar a las clases un proceso de postcompilación.

    Las expresiones de los ponintcuts son similares a una definición de la firma del los métodos, ámbitos de visibilidad, tipos de parámetros y tipo de retorno además del paquete. Es posible hacer expresiones boleanas compuestas para hacer más especifica una expresión. Este pointcut se aplica en la ejecución del método sum de la clase Foo que recibe dos parámetros de tipo int y retorna un valor de tipo int.

    1
    
    execution(int Foo.sum(int,int))
    pointcut.txt

    En la clase Aspects se definen los aspectos con una colección de pointcuts con sus código de advice asociado.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    package io.github.picodotdev.blogbitix.aspects;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Around;
    
    @Aspect
    public class Aspects {
    
        @Before("execution(void Foo.echo())")
        public void echoStart() {
            System.out.println("aspect echo begin");
        }
    
        @After("execution(void Foo.echo())")
        public void echoEnd() {
            System.out.println("aspect echo end");
        }
    
        @Around("execution(int Foo.sum(int,int))")
        public Object log(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("aspect sum begin");
            Object o = pjp.proceed();
            System.out.println("aspect sum end: " + o);
            return o;
        }
    
        @Around("execution(void Foo.sleep())")
        public void time(ProceedingJoinPoint pjp) throws Throwable {
            long start = System.currentTimeMillis();
            Object o = pjp.proceed();
            long end = System.currentTimeMillis();
            System.out.println("aspect time: " + (end - start));
        }
    }
    Aspects.java

    Con la herramienta de construcción Gradle hay que incluir un plugin para aplicar el proceso de weaving. El proceso de weaving consiste en aplicar los aspectos a las clases, AspectJ lo realiza en tiempo de compilación modificando el bytecode de las clases en un segundo paso de compilación, con anterioridad el compilador de Java ha transformado el código fuente de las clases en bytecode.

    1
    2
    3
    4
    5
    6
    7
    
    plugins {
        id 'java'
        id 'application'
        id 'org.springframework.boot' version '2.2.4.RELEASE'
        id 'io.freefair.aspectj.post-compile-weaving' version '4.1.6'
    }
    ...
    build-1.gradle
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    package io.github.picodotdev.blogbitix.aspects;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @SpringBootApplication
    @EnableAspectJAutoProxy
    public class Main implements CommandLineRunner {
    
        ...
    
        @Override
        public void run(String... args) throws Exception {
            // AspectJ
            System.out.println("");
            System.out.println("AspectJ");
            Foo foo = new Foo();
            foo.echo();
            foo.sum(3, 7);
            foo.sleep();
    
            ...
        }
    
        public static void main(String[] args) {
            SpringApplication.run(Main.class, args);
        }
    }
    
    Main-1.java

    En la salida del programa para el apartado de AspectJ se observa que el código de los aspectos se ejecuta al llamar a los métodos de la instancia de la clase Foo.

    1
    2
    3
    4
    5
    6
    7
    
    AspectJ
    aspect echo begin
    echo
    aspect echo end
    aspect sum begin
    aspect sum end: 10
    aspect time: 546
    System.out-1

    Programación orientada a aspectos con Spring AOP

    Spring incluye su solución para la programación orientada a aspectos, más limitada que AspectJ pero suficiente para la mayoría de los casos tampoco requiere aplicar el proceso weaving de AspectJ en tiempo de compilación. La limitación de Spring AOP es que los joint points solo pueden ser métodos. Utiliza las mismas anotaciones de AspectJ para aplicar los aspects en tiempo de ejecución.

    Otra diferenia con AspectJ es que los aspectos se aplican usando proxys que son una clase que envuelve a la instancia a la que se le aplica el aspecto, una vez dentro de la clase objetivo si se llama a otro método de forma interna a ese otro método no se le aplica su aspecto.

    Suponiendo una clase que tiene un méodo foo y bar y desde fuera se llama a foo y este llama a bar para que en llamada desde foo a bar se apliquen los aspectos de bar hay que usar este código. Usar este código implica poner en el código una dependencia a Spring, lo cual no es deseable para el código de dominio.

    1
    2
    3
    4
    5
    
    ...
    public void foo() {
       ((Foo) AopContext.currentProxy()).bar();
    }
    ...
    SpringProxy.java

    En el proxy es donde se ejecuta el código del advice.

    Llamada a un método normal Llamada a un método con un proxy

    Llamada a un método normal y con un proxy

    Para que Spring procese las anotaciones require usar la anotación @EnableAspectJAutoProxy y que Spring encuentre la clase de los aspectos, anotándola con @Component o devolviendo una instancia en el contenedor de dependencias como en este caso.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    package io.github.picodotdev.blogbitix.aspects;
    
    ...
    
    @SpringBootApplication
    @EnableAspectJAutoProxy
    public class Main implements CommandLineRunner {
    
        ...
    
        @Bean
        public Foo foo() {
            return new Foo();
        }
    
        @Bean
        public Aspects aspects() {
            return new Aspects();
        }
    
        @Override
        public void run(String... args) throws Exception {
            ...
    
            // Spring AOP
            System.out.println("");
            System.out.println("Spring AOP (AspectJ anotations)");
            fooBean.echo();
            fooBean.sum(3, 7);
            fooBean.sleep();
    
            ...
        }
    }
    
    Main-2.java

    El plugin para realizar el proceso de weaving con AspectJ no es necesario. Spring realiza e proceso de weaving en tiempo de ejecución.

    1
    2
    3
    4
    5
    6
    7
    
    plugins {
        id 'java'
        id 'application'
        id 'org.springframework.boot' version '2.2.4.RELEASE'
        //id 'io.freefair.aspectj.post-compile-weaving' version '4.1.6'
    }
    ...
    build-2.gradle

    El resultado es el mismo que con AspectJ.

    1
    2
    3
    4
    5
    6
    7
    
    Spring AOP (AspectJ anotations)
    aspect echo begin
    echo
    aspect echo end
    aspect sum begin
    aspect sum end: 10
    aspect time: 1049
    System.out-2

    Programación orientada a aspectos con la clase Proxy

    Para casos muy sencillos donde no sea posible aplicar una de las opciones anteriores al no poder usar sus librerías por restricciones del proyecto en cuanto a dependencias usables está la alternativa incluida en el JDK. La clase Proxy está incorporada en el propio JDK, permite hacer cosas sencillas sin dependencias adicionales.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    package io.github.picodotdev.blogbitix.aspects;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class LogProxy implements InvocationHandler {
    
        protected Object object;
        protected Proxy proxy;
    
        public LogProxy(Object object) {
            this.object = object;
            proxy = (Proxy) proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
        }
    
        public Proxy getProxy() {
            return proxy;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("proxy " + method.getName() + " begin");
            Object o = method.invoke(object, args);
            System.out.print("proxy " + method.getName() + " end");
            if (!method.getReturnType().equals(Void.TYPE)) {
                System.out.print(": " + o);
            }
            System.out.println();
            return o;
        }
    }
    LogProxy.java
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    package io.github.picodotdev.blogbitix.aspects;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProfileProxy implements InvocationHandler {
    
        protected Object object;
        protected Proxy proxy;
    
        public ProfileProxy(Object object) {
            this.object = object;
            proxy = (Proxy) proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
        }
    
        public Proxy getProxy() {
            return proxy;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long start = System.currentTimeMillis();
            Object o = method.invoke(object, args);
            long end = System.currentTimeMillis();
            if (method.getName().equals("sleep")) {
                System.out.println("proxy time: " + (end - start));
            }
            return o;
        }
    }
    ProfileProxy.java

    En este caso se observa que se ha aplicado el aspecto de AspectJ y además los aspectos de los proxys de este apartado.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    Java Proxy
    proxy echo begin
    aspect echo begin
    echo
    aspect echo end
    proxy echo end
    proxy sum begin
    aspect sum begin
    aspect sum end: 10
    proxy sum end: 10
    proxy sleep begin
    aspect time: 323
    proxy time: 323
    proxy sleep end
    System.out-3

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

    » Leer más, comentarios, etc...

    Blog Bitix

    10º aniversario del blog

    febrero 06, 2020 06:00

    Hugo

    “Bueno, ¡pues ya está!, ya tengo mi propio blog, todavía está en fase de construcción así que según vaya teniendo tiempo iré haciendo pequeños cambios en los gadgets y en su disposición. Por ahora me conformo con tenerlo visible […]. Pero lo principal, escribir entradas en el blog, es algo que ya puedo realizar. Tratarán sobre temas relacionados con el software libre, incluyendo las dos distribuciones GNU/Linux que en este momento uso más habitualmente que son Ubuntu y Arch Linux, temas de tecnología, quizá temas de programación, experiencias personales sobre los mismos y otras cosas que se me vayan ocurriendo. […]".

    Con el texto anterior empezaba el primer artículo a modo de prestación en elblogdepicodev en el 2010, este mes de febrero se cumple que hace 10 años creé y empecé a escribir en un blog, durante todo este tiempo habrá habido pocas semanas de las 520 que no he escrito al menos un artículo, es más el total de artículos que he escrito desde entonces ha sido de unos 650 artículos dado que algunas semanas he publicado dos artículos y algunas esporádicamente incluso tres. En total 2,5 millones de páginas vistas, 525K en el último año. Cuando miro Google Analytics y veo artículos del año 2017, 2016, 2015, 2014, … que se siguen visitado, no me da la sensación de que hayan pasado varios años desde que los publique.

    Pasados estos 10 años y a pesar del tiempo que requiere escribir y publicar cada artículo aún sigo con ganas de seguir escribiendo y publicando, hay una enorme cantidad de temas interesantes sobre los que escribir, no me he planteado dejarlo, y es que me permite seguir aprendiendo cada semana un poco sobre alguna cosa que me interesa, me gusta compartir con la intención de que a alguien le resulta útil el contenido y quizá en un futuro esto mismo que escribo me resulte útil laboralmente. La temáticas principales siguen siendo Java y GNU/Linux aunque no es un blog solo de Java ni un blog solo de GNU/Linux. Como blog personal en el sentido de individual que es, muy rara vez incluyo información personal, en él ocasionalmente hago desempaquetados de los productos que compro y he probado, algún artículo de opinión o desde hace unos meses que tengo una PS4 escribiré sobre los juegos que voy jugando y completando.

    Como comentaba en la última hemeroteca del año 2019 el blog no ha crecido en visitas a pesar de haber escrito unos 80 artículos nuevos, por ello quiero hacer algunos cambios. El primero es planificar mejor el contenido que escribo y publico con el objetivo en parte de atraer más visitas que al mismo tiempo sean interesantes de leer pero también manteniendo que el escribir lo sea para mi. Hasta ahora he escrito de lo que me ha apetecido en cada momento de forma un tanto aleatoria sin ningún plan de publicación incluso para las series de artículos relacionados como la serie de Docker o la serie sobre GraphQL. Analizando los artículos más vistos alguno de los más sencillos son los más populares de mi blog como 4 formas de hacer un bucle for en Java, me plantearé escribir según se me vayan ocurriendo alguno de estos artículos sencillos, básicos o no sencillos pero esenciales que en algún caso no escribo por ya darlo por sabido. Tampoco quiero dejar de escribir por completo de vez en cuando algún artículo que para mi considero avanzado.

    El segundo punto que ya he empezado, es hacer algunas modificaciones en los estilos del blog para mejorar el porcentaje de rebote, que creo es muy alto casi de un 90%, y aumentar el número de páginas vistas por sesión. Para esto tengo pensado modificar las páginas de lista de artículos como en la página de inicio y los artículos relacionados al final de cada artículo. En vez de que sean un simple enlace que sean el título y una imagen que traigan algo más la atención a dos o tres columnas en vez de a una solo para que quepan más en lugar de un artículo en el mismo espacio. Quizá incluiré en algunas páginas un slider que incluya artículos que considero destacables también para mejorar el porcentaje de rebote y para que artículos antiguos sean un poco más visibles. También mejorar la página de error 404 con estas mismas ideas, que aunque no debería llegarse a ella si se realizan algunas visualizaciones, ahora solo muestro un mensaje sin ningún enlace salvo los de la estructura de la página.

    Esos son los estilos por los que ha pasado el blog.

    El blog de pico.dev con Blogger Blog Bitix con Octopress Blog Bitix con Hugo

    Rediseños que he ido realizando en el blog

    Algunas otras modificaciones ya he realizado, una de ellas crear la versión de páginas AMP del blog para ver que resultado da. En un principio esta versión está mejor adaptada para los móviles, siendo más rápida, con lo que para Google es posible que la considere mejor, y dado que Google es la mayor fuente de usuarios quizá se note algo. Al final de año mediré con Analytics cual ha sido el resultado. En las primeras semanas algunos usuarios ya están accediendo a la versión AMP.

    Otra modificación importante que he realizado ha sido cargar las imágenes, los comentarios de Disqus e iframes como vídeos de YouTube con lazy load. Así estos elementos no se solicitan hasta que realmente son necesarios al ser visualizados, lo que hace que la página se cargue antes y sea más rápida. Mejora la métrica de PageSpeed y Google en su algoritmo de posicionamiento es posible que las posicione mejor en su página de resultados.

    Con algunas nuevas capacidades de Hugo como los render hooks de enlaces e imágenes es posible implementar cierta lógica para algunas cosas interesantes que simplifiquen la generación del contenido. Aún estoy pendiente de que Hugo permita usar el formato de imágenes WebP y soporte watermarking.

    Vamos a ver que ocurre en la década del 2020.

    » Leer más, comentarios, etc...

    Navegapolis

    No copie los modelos ágiles de los expertos o de otras empresas

    febrero 04, 2020 04:11

    No copie modelos
    • No copie los métodos y prácticas de otras empresas, ni implemente un modelo diseñado por expertos. Estudie y aprenda de ellos, pero experimente y adáptese a lo que funciona para usted y su cultura.
    • No contrate a una gran empresa de consultoría para transformar su organización de manera expédita o para implementar nuevas metodologías o prácticas... sus equipos no desarrollarán la confianza o la capacidad de sostener, continuar mejorando o adaptar y desarrollar nuevos procesos y comportamientos por su cuenta.
    • Desarrolle sus propios coaches. Inicialmente puede que necesite contratar a un coach externo para establecer una base sólida, pero en última instancia el agente del cambio debe ser propio. La intensidad del coahing es clave para el mantenimiento y el crecimiento.


    A estas conclusiones llegan Nicole Forsgren, Jez Humble y Gene Kim en 2017 tras analizar durante 4 años el trabajo en 2.000 organizaciones TIC, comprendiendo desde pequeñas startups de menos de 5 empleados, hasta grandes compañías de 10.000 y cuyos resultados publican en su libro Accelerate.

     

    » Leer más, comentarios, etc...

    Navegapolis

    Adapte los modelos ágiles a lo que funciona para usted y su cultura.

    febrero 04, 2020 04:11

    No copie modelos
    • No copie los métodos y prácticas de otras empresas, ni implemente un modelo diseñado por expertos. Estudie y aprenda de ellos, pero experimente y adáptese a lo que funciona para usted y su cultura.
    • No contrate a una gran empresa de consultoría para transformar su organización de manera expédita o para implementar nuevas metodologías o prácticas... sus equipos no desarrollarán la confianza o la capacidad de sostener, continuar mejorando o adaptar y desarrollar nuevos procesos y comportamientos por su cuenta.
    • Desarrolle sus propios coaches. Inicialmente puede que necesite contratar a un coach externo para establecer una base sólida, pero en última instancia el agente del cambio debe ser propio. La intensidad del coahing es clave para el mantenimiento y el crecimiento.


    A estas conclusiones llegan Nicole Forsgren, Jez Humble y Gene Kim en 2017 tras analizar durante 4 años el trabajo en 2.000 organizaciones TIC, comprendiendo desde pequeñas startups de menos de 5 empleados, hasta grandes compañías de 10.000 y cuyos resultados publican en su libro Accelerate.

     

    » Leer más, comentarios, etc...

    Picando Código

    [Libro] The Rise and Fall of the Dinosaurs por Steve Brusatte

    febrero 04, 2020 02:30

    Después de visitar la exhibición de Tiranosaurios en el Museo Nacional de Escocia, pasé por la tienda de regalos. Con cada exhibición, la tienda vende artículos relacionados: juguetes, camisetas, libros para nínos y también libros científicos sobre el tema en particular. Entre estos libros me encontré con el de Steve Brusatte, paleontólogo cuyo trabajo podemos encontrar en la exhibición mencionada. The Rise and Fall of the Dinosaurs (El Auge y Caída de los Dinosaurios, ¡también disponible en español!) es un libro que recorre toda la historia de los dinosaurios. Desde el período Paleozoico (y un poco antes) donde gran parte de la tierra se concentraba en el supercontinente de Pangea, hasta su extinción y cómo fueron evolucionando a lo largo de sus millones de años de dominio sobre el planeta.

    Es un libro extremadamente entretenido, está basado en descubrimientos científicos y datos, pero todo está contado desde la experiencia del autor a través de historias. Así que no es totalmente académico, lo cual podría resultar aburrido, a pesar de estar basado en conocimiento científico.

    The Rise and Fall of the Dinosaurs - Steve Brusatte

     

    ¡Hay mucho para aprender! Por más que sepamos sobre dinosaurios y otros animales prehistóricos la paleontología está en constante evolución. Particularmente estos días se encuentra -en promedio- una especie nueva de dinosaurio por semana (dato que aprendí en el libro). Así que es una buena forma de refrescar conocimientos y estar al tanto de las última teorías sobre la evolución en la tierra.

    Entre las anécdotas e historias interesantes, el autor habla sobre cómo se descubrió el Herrerasaurio en Argentina. Fue en el Valle de la Luna en la provincia de San Juan. Tengo grabado ese lugar en la memoria de haber leído sobre fósiles en alguna de las revistas científicas que leía cuando era chico (Conozca Más, Descubrir, Muy Interesante). Pero habiendo sido “fanático de los dinosaurios” desde chico, fue un nombre familiar que me hizo conectar lo nuevo que estaba leyendo con algo muy específico del pasado. También cuenta la historia sobre una expedición a la Isla de Skye en Escocia que me resultó particularmente entretenida. Voy a tener que ir, visitar el museo del que habla y salir a explorar para buscar huellas o fósiles de dinosaurios.

    En el libro vamos a conocer más sobre los dinosaurios, pero también aprender cómo el período Pérmico (que vino antes de los dinosaurios y terminó con una extinción muchísimo más masiva que la que mató a los dinosaurios) fue igualmente fascinante, así como lo fueron los dinosauriomorfos y otras especies. Cuando se adentra en el tema, reaviva la fascinación por los dinosaurios. ¿Por qué/cómo los Saurópodos se volvieron tan grandes? ¿Cómo podemos imaginarlos o concebir animales de tal tamaño? Los capítulos sobre los Tiranosaurios, el T-Rex y toda su familia, son fascinantes y alimentan todavía más la admiración por la especie y dejan ver el amor incondicional que varios estudiosos le tienen a ésta especie. Es esa magia que tienen los dinosaurios, fueron animales reales, con características muy distintas y muy similares a los animales de hoy, pero sólo podemos teorizar e imaginarlos (por ahora).

    En varias oportunidades paraba de leer para ir a mi libro de referencia o a internet para aprender más sobre una especie en particular. Mi libro de referencia actual es una Enciclopedia Ilustrada por Dougal Dixon (autor escocés), con 355 especies. Pero creo que no está muy actualizado… (¡los Tiranosaurios en el libro no tienen plumas!). Una de las cosas que disfruto de leer libros científicos es que me hace pensar en cosas que de repente no me hubiera cuestionado antes. Fue así que en un momento empecé a plantearme, ¿cómo carajos en algún punto de la historia un animal de repente empezó a volar? ¿Cómo se llegó hasta ahí? Específicamente qué cambió para que un animal terminara recorriendo los cielos. Potencialmente el autor haya plantado la semilla de esa idea a propósito, porque el capítulo 8 “Los dinosaurios toman vuelo” encontramos respuestas a esas preguntas.

    Siguiendo con el tema de vuelo, desde que se introdujo masivamente la teoría de que los pájaros habían evolucionado de los dinosaurios tengo una nueva apreciación por éstos animales. Pero no sólo evolucionaron de los dinosaurios, son dinosaurios modernos, evolución de aquellos que sobrevivieron la extinción. Y es otra cosa interesante que reafirma el libro, los tenemos acá revoloteando entre nosotros.

    Steve Brusatte se para en los hombros de gigantes, algo intrínseco en el mundo científico. Pero el autor lo demuestra en cada oportunidad que tiene. Referencia y reconoce a colegas y amigos del rubro a lo largo de todo el libro. Incluso me quedé con uno o dos títulos que menciona para buscar y leer más adelante.

    El Auge y Caída de los Dinosaurios es un excelente libro que nos enseña lo último en materia de dinosaurios en un mundo post Jurassic Park. Lo recomiento ampliamente si alguna vez le gustaron o todavía le gustan los dinosaurios. Si disfrutaron cualquiera de las películas de Jurassic Park, probablemente disfruten este libro. Léanlo aunque sea por corregir las inexactitudes científicas que nos hicieron creer las películas, como que un T-Rex no nos vería si nos quedamos quietos. Si les interesa la ciencia, los animales, y la historia de nuestro planeta, probablemente también disfruten este libro.

    Steve Brusatte nació en Illinois, Estados Unidos pero vive en Escocia dando clases en la Universidad de Edimburgo. Pueden seguirlo en Twitter en @SteveBrusatte. Les recomiendo buscar el libro en su biblioteca o librería favorita, y como comentaba antes, está disponible también en español.

    » Leer más, comentarios, etc...

    Variable not found

    Cómo documentar y generar código cliente de nuestras APIs utilizando Swagger/OpenAPI

    febrero 04, 2020 07:29

    Open API Días atrás veíamos cómo utilizar Swashbuckle para generar automáticamente la descripción OpenAPI de APIs creadas con ASP.NET Core MVC y ponerlas a disposición de los consumidores en una ruta específica:
      Descripción OpenAPI de una API

    En este post vamos a ver un par de formas de sacar provecho de esta descripción:
    • En primer lugar, usaremos Swagger UI para generar un sitio web interactivo de documentación y pruebas de nuestra API.
    • Después, veremos cómo generar desde Visual Studio código cliente para acceder a la API.

    Swagger UI

    Swagger UI es un proyecto open source que permite generar una web de documentación partiendo de la descripción OpenAPI de un servicio. Además esta web es interactiva, pues incluye la posibilidad de probar "in situ" las llamadas a los servicios.

    Ponerlo en marcha es trivial: una vez tenemos instalado el paquete Swashbuckle.AspNetCore en nuestro proyecto y hemos configurado apropiadamente la generación de la definición OpenAPI (ya lo vimos en el post anterior), basta con añadir al pipeline el middleware encargado de generar la web:
    ...
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
    c.SwaggerEndpoint("/swagger/api/swagger.json", "Awesomic calculator");
    });
    ...
    Fijaos que el endpoint que especificamos en la llamada a SwaggerEndpoint() es la ruta hacia la descripción OpenAPI de nuestro servicio.

    Hecho esto, ya podemos utilizar la ruta "/swagger" para tener acceso a la documentación interactiva de la API:

    Página de la API Calculator en Swagger UI

    Pulsando sobre las operaciones del servicio de cálculo podemos acceder a la descripción completa de las mismas. Ahí encontraremos la información textual incluida en comentarios XML en el código, los parámetros de entrada, respuestas HTTP posibles, etc:

    El servicio Divide en Swagger UI
    Tipos de respuesta del servicio

    Para probar en vivo el servicio, podemos pulsar el botón "Try it out"; podremos especificar el valor para los distintos parámetros de entrada, y acceder después a la información retornada desde el mismo:

    Probando en vivo el servicio a través de Swagger UI

    Generación de código cliente para acceder a la API

    Visual Studio incluye herramientas para la generación de código cliente de las APIs definidas mediante OpenAPI. He de decir que a veces parecen no funcionar demasiado bien, pero al menos cumplen mínimamente su objetivo de ahorrarnos trabajo a la hora de implementar clientes de las API.

    La generación se realiza partiendo de los esquemas JSON que describen los servicios, que pueden encontrarse ya en nuestro proyecto, o bien ser descargados utilizando el IDE, accediendo a la opción Dependencies > Add Connected Service > Service References; desde ese punto podremos añadir servicios OpenAPI simplemente indicando su ruta en el disco o la URL de su definición:

    Añadir referencia a servicio OpenAPI

    Tras ello, Visual Studio acudirá al origen indicado, descargará el archivo y lo añadirá al proyecto en la carpeta OpenAPI estableciendo su Build action a "OpenAPI file reference", lo cual asegurará que el cliente sea generado de forma automática:

    Esquema OpenAPI añadido
    Si, en lugar de dejar que Visual Studio descargue el esquema y lo incluya en el proyecto, preferimos incluirlo nosotros manualmente, para que todo funcione obligatoriamente tendremos que modificar la Build Action a la indicada.
    En las propiedades del archivo JSON podemos establecer también determinadas opciones, como el nombre de la clase generada o el espacio de nombres en el que será incluida. En nuestro caso, podría ser razonable establecer el nombre de la clase generada y, opcionalmente, el namespace:

    Esquema OpenAPI añadido

    Es importante tener en cuenta que las clases generadas serán ubicadas en la carpeta "Obj" del proyecto, por lo que no estarán disponibles a nivel de código en la solución.

    Una vez realizados los pasos anteriores, ya podemos utilizar directamente el cliente de la API. Como vemos en el siguiente código, se trata de una clase con el nombre que hemos indicado, que instanciamos suministrándole la URL base del servicio y la instancia de HttpClient que se utilizará internamente para realizar las llamadas:
    class Program
    {
    static async Task Main(string[] args)
    {
    var httpClient = new HttpClient();
    var x = new CalculatorApi("https://localhost:44329/", httpClient);
    var result = await x.DivideAsync(100, 10);
    Console.WriteLine(result);
    }
    }
    Publicado en Variable not found.

    » Leer más, comentarios, etc...

    Variable not found

    Enlaces interesantes 389

    febrero 03, 2020 07:47

    Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

    Por si te lo perdiste...

    .NET Core / .NET

    ASP.NET Core / ASP.NET

    Azure / Cloud

    Conceptos / Patrones / Buenas prácticas

    Data

    Machine learning / IA / Bots

    Web / HTML / CSS / Javascript

    Visual Studio / Complementos / Herramientas

    Xamarin

    Otros

    Publicado en Variable not found.

    » Leer más, comentarios, etc...

    Picando Código

    ATuServicio 2020

    febrero 01, 2020 03:41

    Como casi todos los 1° de febrero desde 2015, este año se lanza una versión actualizada de ATuServicio. Y como todos los meses de enero de los últimos años, con DATA anduvimos a las corridas arreglando, actualizando y corrigiendo cosas para poder tener todo en marcha. Se van a venir algunas cosas nuevas en breve, pero mientras tanto ya se pueden ver los datos actualizados ahora que se volvió a abrir el corralito mutual en Uruguay.

    ATuServicio.uy

     

    Copio y pego el mensaje de DATA:

    Empieza un nuevo período de movilidad regulada (MORE), también conocido como “corralito”, y con él vuelve A Tu Servicio, como desde hace seis años.

    Desde hoy, sábado 1º, y hasta el 28 de febrero, los/as usuarios/as del sistema de salud que cumplan con los requisitos, podrán cambiar de prestador de salud si así lo desean. Las personas interesadas en hacerlo deberán presentarse con su cédula de identidad en la mutualista a la cual quieren ingresar.

    Decisiones informadas y basadas en datos

    A Tu Servicio es una herramienta que permite obtener, visualizar y comparar datos de los diferentes prestadores de salud de Uruguay, para promover la toma de decisiones informadas de los/as usuarios/as y la transparencia del sistema.

    Por eso, si estás pensando en cambiar de prestador, te invitamos a ingresar a la herramienta para comprar datos y así definir qué es lo que más te conviene, según las prioridades que vos definas. Y si no, igual es una gran oportunidad para conocer más sobre tu prestador de salud 😁.

    Esta plataforma es desarrollada por DATA y el Ministerio de Salud Pública.

    Más información:

    » Leer más, comentarios, etc...

    Picando Código

    Exhibición sobre Tiranosaurios en el Museo Nacional de Escocia

    enero 29, 2020 02:30

    Del 23 de enero al 4 de mayo el Museo Nacional de Escocia en Edimburgo está presentando una exhibición sobre Tiranosaurios 🦖

    Mientras que la especie más famosa es el poderoso T. rex, los tiranosaurios venían en todas formas y tamaños, y su historia se extiende por más de 100 millones de años. La exhibición muestra especímenes fósiles extremadamente raros, esqueletos de yeso – incluyendo uno de “Scotty”, uno de los esqueletos de T. rex más grandes y completos del mundo – e increíbles modelos de dinosaurios con plumas. Los visitantes también podrán explorar la diversidad de cráneos de tiranosaurio y encontrar lo que variaciones en la estructura nos pueden decir sobre distintas estrategias de caza y alimentación.

    Más información.

    El domingo estuve por el museo y disfruté por algunas horas un tiempo con estos fósiles y aprendiendo más sobre la familia Tiranosauria. Por suerte he tenido la oportunidad de ver más de un modelo de esqueleto de Tiranosaurio. El último había sido recientemente Tristan Otto en Berlín. El de Scotty es bastante impresionante, y está bueno ver su tamaño en comparación a otros especímenes de la familia. Me encanta presenciar esos modelos e imaginar lo que sería estar frente a un animal de ese tipo.

    Las placas informativas de la exhibición incluian información de lo más actualizado que se conoce sobre la familia de Tiranosaurios. También hay menúes interactivos y demás para los más pequeños (y no tan pequeños). Hay también unos modelos de tiranosaurio con plumas como dice la descripción. Esto es algo que no se sabía hasta hace unos años y ahora se cree que todos o la mayoría de los tiranosaurios eran emplumados.

    Algunas fotos de la exhibición:

    Cráneos de Tiranosaurio

    Cráneos de Tiranosaurio

    Tiranosaurios varios

    Tiranosaurios varios

    Más Tiranosaurios

    Más Tiranosaurios

    Modelo de Scotty el Tiranosaurio

    Modelo de Scotty el Tiranosaurio

    Modelo de Scotty el Tiranosaurio

    Modelo de Scotty el Tiranosaurio

    El Museo Nacional de Escocia tiene entrada gratuita (aunque se sugiere dejar alguna donación), y es espectacular. Así que cuando anden por Edimburgo, es un paseo súper recomendable. 

    Cuentan con su propio modelo permanente de Tiranosaurio Rex basado en MOR 555, también conocido como “Wankel Rex”. Con un 46% del esqueleto encontrado, es el segundo esqueleto más grande y más completo de T. Rex (más información en el sitio del museo). También hay un modelo de Estegosaurio y un montón de cosas interesantes más. Ya que estaba en el museo pasé a saludar a Wankel para que no se pusiera celoso del resto de la exhibición…

    MOR 555 o "Wankel Rex"

    MOR 555 o “Wankel Rex”

    » Leer más, comentarios, etc...

    Picando Código

    Mozilla Thunderbird se muda a MZLA Technologies Corporation

    enero 28, 2020 09:31

    Mozilla Thunderbird

    Mi cliente de correo favorito, Thunderbird, encontró un nuevo hogar:

    A partir de hoy, el proyecto Thunderbird operará desde una subsidiaria completamente nueva de la Fundación Mozilla, MZLA Technologies Corporation. Esta mudanza se ha estado trabajando por un tiempo a lo que Thunderbird ha crecido en donaciones, staff y aspiraciones. Esto no va a impactar las actividades día-a-día o la misión: Thunderbird seguirá siendo libre y de código abierto, con el mismo calendario de lanzamientos y gente detrás del proyecto.

    Hubo un tiempo en el que el futuro de Thunderbird era incierto, y no estaba claro qué iba a pasar con el proyecto tras la decisión de que Mozilla Corporation no lo financiaría. Pero en años recientes las donaciones de usuarios de Thunderbird le han permitido crecer y florecer orgánicamente dentro de la Fundación Mozilla. Ahora, para asegurar el éxito operacional a futuro, siguiendo meses de planificación, se está forjando un nuevo camino hacia adelante. Mudarse a la Corporación MZLA Technologies no sólo le permitirá al proyecto Thunderbird más flexibilidad y agilidad, sino que también permitirá explorar la oferta de productos y servicios a los usuarios que no eran posibles bajo la Fundación Mozilla. La movida permitirá al proyecto recaudar ingresos a través de asociaciones y donaciones no-caritativas, lo cual a cambio puede ser usado para cubrir los costos de nuevos productos y servicios.

    El enfoque de Thunderbird no va a cambiar. Se mantiene comprometido a crear tecnología increíble de código abierto enfocada en estándares abiertos, privacidad del usuario y comunicación productiva. El Consejo de Thunderbird continua administrando el proyecto, y el equipo que guía el desarrollo de Thunderbird sigue siendo el mismo.

    Por último, esta mudanza a la Corporación MZLA Technolocies permite al proyecto Thunderbird contratar con mayor facilidad, actuar de manera más rápida, y perseguir ideas que previamente no eran posibles. Más información sobre la dirección futura de Thunderbird será compartida en próximos meses.

    Thunderbird Blog: Thunderbird’s New Home

    Es bueno ver que el cliente de correo tenga un futuro asegurado, sobre todo después de ese período en el que su destino era bastante incierto. Por suerte con el software libre generalmente éstos proyectos tan populares terminan encontrando un camino. Pero aplicaciones tan complejas siempre necesitan un poco de respaldo para seguir adelante.  Habrá que mantenerse atentos a las novedades que trae el cambio.

    » Leer más, comentarios, etc...

    Fixed Buffer

    Cómo hacer mock de ILogger en .Net Core 3.x durante las pruebas

    enero 28, 2020 09:00

    Tiempo de lectura: 5 minutos
    Imagen ornamental para la entrada "Cómo hacer mock de ILogger en .Net Core 3.x durante las pruebas" donde se ven unas trazas de un log

    Este mes he estado trabajando en un nuevo proyecto para un cliente que consiste en un servicio totalmente desatendido. Como servicio desatendido que es necesita un sistema de logging muy integrado de modo que haya toda la información posible en caso de fallo porque corre en equipos de difícil acceso. Funcionalidad a funcionalidad voy añadiendo las pruebas al código y me encuentro con una no muy agradable sorpresa… Los cambios en las clases de .Net Core 3 hacen que ILogger no se pueda mockear, al menos no a simple vista…

    Por dar algo de contexto, el servicio lo he implementado con un Worker Service de .Net Core 3.1 por lo que la opción natural es registrar el logger dentro de la infraestructura de .Net Core 3.1 a través de ILogger y utiliza Entity Framework Core Code First.

    ¿Cómo se hacia mock de ILogger en .Net Core 2.x?

    Para poder hacer las pruebas sobre código que utiliza ILogger teníamos (y tenemos en .Net Core 3.x) una implementación genérica que no hace nada para poder hacer las pruebas. Esta implementación es NullLogger y gracias a ella las pruebas van a poder lanzarse aunque el logger no haga nada. Si por el contrario necesitamos hacer algo sobre el logger, sea una configuración de callback para loguear a través de la consola de runner o simplemente verificaciones sobre los mensajes, vamos a tener que hacer nuestro propio mock.

    Pese a que no era algo especialmente sencillo, hacer un mock de ILogger en .Net Core 2.x era posible sin tener que implementar código extra. La interfaz ILogger tiene entre muchos métodos de extensión, un método con una firma así:

    public void Log<TState> (LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState,Exception,string> formatter);
    

    Sobre esta interfaz, el tipo genérico con el que se llama a esta interfaz es ‘FormattedLogValues‘. Sabiendo esta información se puede hacer un mock perfectamente con librerías como por ejemplo Moq sin mucho esfuerzo:

    var logger = Mock.Of<ILogger<Program>>();
    //Configuración
    Mock.Get(logger).Setup(
               m => m.Log(
               It.IsAny<LogLevel>(),
               It.IsAny<EventId>(),
               It.IsAny<FormattedLogValues>(),
               It.IsAny<Exception>(),
               It.IsAny<Func<object, Exception, string>>()
               )
          ).Callback(/*Callback*/);
    //Verificación
    Mock.Get(logger).Verify(
               m => m.Log(
               It.IsAny<LogLevel>(),
               It.IsAny<EventId>(),
               It.IsAny<FormattedLogValues>(),
               It.IsAny<Exception>(),
               It.IsAny<Func<object, Exception, string>>()
               )
          );
    

    El código es perfectamente funcional y con el se pueden escribir cualquier requisito para las pruebas.

    ¿Y por qué no se puede en .Net Core 3.x?

    Si como comentábamos hace poco la interfaz no ha cambiado, y los métodos no han cambiado, ¿dónde está el problema? ¿Si pongo ese mismo código en mis pruebas porque no va a funcionar? Pues el problema está en que entre .Net Core 2 y 3 han cambiado algunas clases y estructuras, entre ellas ‘FormattedLogValues’ que ha pasado de ser pública a ser interna.

    Esto hace que si ponemos el mismo código, pero utilizando las versiones de los paquetes para .Net Core 3.x, tengamos un error como este:

    La imagen muestra el error de accesibilidad al tratar de hacer mock a ILogger en .Net Core 3.x

    Si bien es cierto que los frameworks de mocking se adaptan para dar el mejor servicio posible ante estos cambios, no siempre son suficientes. Por ejemplo, para la parte de verificación Moq ya tiene una solución para reemplazar la prueba y es tan simple como esto:

    Mock.Get(logger).Verify(
                      log => log.Log(
                        It.IsAny<LogLevel>(),
                        It.IsAny<EventId>(),
                        It.IsAny<It.IsAnyType>(),
                        It.IsAny<Exception>(),
                        (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
                      Times.Once);
    

    ¿Cómo hacer mock de ILogger en .Net Core 3.x?

    En mi caso concreto esta funcionalidad de verificación no fue suficiente ya que estaba teniendo problemas con las pruebas en el entorno de integración que no se replicaban en ninguna otra máquina. Para poder aprovechar toda la información de log que el propio código genera necesitaba relanzar el logger hacia la consola del runner de xUnit.

    En versiones anteriores de .Net Core me hubiese valido utilizar un callback del método ‘Log’, pero con el cambio de ‘FormattedLogValues’ no es una opción. Precisamente por eso he tenido que hacer mi propia implementación de ILogger basada en la del NullLogger, pero con los extras bajo el capó:

    public class SpyLogger<T> : ILogger<T>
    {
        private readonly ITestOutputHelper _outputHelper;
    
        public SpyLogger(ITestOutputHelper outputHelper)
        {
            _outputHelper = outputHelper;
        }
    
        public IDisposable BeginScope<TState>(TState state)
        {
            return NullScope.Instance;
        }
    
        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }
    
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            _outputHelper.WriteLine($"{DateTime.Now.TimeOfDay}--{typeof(T).Name}-->{state}");
        }
    }
    internal class NullScope : IDisposable
    {
        public static NullScope Instance { get; } = new NullScope();
    
        private NullScope()
        {
        }
        public void Dispose()
        {
        }
    }
    

    En este código las cosas importantes son 2. La primera es la clase ‘NullScope’ que es la misma implementación que tiene ‘NullLogger’. Esta clase es necesaria para la implementación de ‘BeginScope’ de nuestro propio ILogger. La segunda es que mi ‘SpyLogger’ recibe como parámetro el logger de xUnit y escribe sobre él. Con esto la salida de las pruebas registra todos los mensajes de log que registraría la aplicación y tengo una información muy valiosa sobre la ejecución de las pruebas.

    La imagen muestra varias trazas de log en la consola de xUnit conseguidas gracias a hacer el mock de ILogger en .Net Core 3.1

    Con esta clase ‘SpyLogger’ he conseguido hacer un mock sobre ILogger en .Net Core 3.x y relanzar los mensajes hacia la consola del runner de xUnit.

    Aunque en este caso concreto la necesidad era relanzar las trazas de log hacia la consola de xUnit, esta puede ser la implementación base sobre la que añadir diferentes funcionalidades. Por ejemplo, si hacer verificaciones fuese necesario además de la funcionalidad que ya hay, se podría hacer el código necesario para las verificaciones o incluso recibir un ILogger en el constructor y cambiar el método Log para que llame de nuevo al ILogger. El cambio sería que ahora ese ILogger recibido como parámetro en el constructor si sería un mock normal de Moq sobre el que si podemos hacer verificaciones.

    Aunque el código es un poco enrevesado y hay que añadir una capa extra de trabajo, es la mejor solución que he encontrado y quería compartirla por si a alguien le puede ayudar a afrontar el problema con el que me he encontrado yo.

    Si por el contrario ya te ha tocado enfrentar este problema de hacer mock de ILogger en .Net Core 3 y crees que tu solución es mejor, no dudes en dejar un comentario planteándola.

    En la próxima entrada hablaremos de un tema muy interesante como es el escribir código de alto rendimiento con .Net Core. ¡No te lo pierdas!

    **La entrada Cómo hacer mock de ILogger en .Net Core 3.x durante las pruebas se publicó primero en Fixed Buffer.**

    » Leer más, comentarios, etc...

    Variable not found

    Describiendo APIs ASP.NET Core con Swagger

    enero 28, 2020 07:05

    Open APILa OpenAPI Specification (OAS) es un estándar abierto para la descripción de APIS REST promovido por la Iniciativa OpenAPI, un consorcio de compañías de primer nivel como Microsoft, Google, IBM, Paypal y otros.

    El objetivo de OpenAPI es conseguir una fórmula normalizada para describir las capacidades de un servicio REST, independientemente de los lenguajes o tecnologías con las que sea implementado. Esta descripción, normalmente especificada en formato JSON o YAML, permite a clientes (sean humanos o máquinas) descubrir servicios, comprender sus capacidades y conocer sus detalles de funcionamiento sin necesidad de tener acceso a su código fuente o documentación textual.

    Esta especificación formal abre interesantes posibilidades, pues a partir de la definición estandarizada de servicios es posible, entre otros,
    • disponer de herramientas de diseño y modelado de servicios,
    • generar automáticamente páginas de documentación,
    • generar automáticamente código cliente y servidor para dichos servicios para distintos, frameworks y lenguajes de programación,
    • generar automáticamente código de testing y validaciones,
    • o generar automáticamente mocks de servicios.

    ¿Y qué tiene que ver Swagger en todo esto? Pues bastante, porque Swagger fue la semilla de OpenAPI. De hecho, Swagger era un proyecto liderado por la compañía Smartbear, que definió las dos primeras versiones de la especificación, y luego la donó a la nueva iniciativa abierta OpenAPI, quedando renombrada a partir de ese momento a "OpenAPI Specification". Por tanto, a efectos prácticos, cuando hablamos de la especificación Swagger y de OpenAPI, en muchas ocasiones nos estamos refiriendo a lo mismo.

    A partir de la puesta en marcha de la iniciativa OpenAPI, el término Swagger pasó a utilizarse principalmente para denominar el framework o conjunto de herramientas que implementan la especificación, tanto de forma comercial como open source: diseñadores, generadores de código, parsers, generadores de documentación, etc.

    La web Swagger.io es el sitio de referencia para las herramientas básicas y open source de Swagger.

    Herramientas en Swagger.io

    ¿Qué pinta tiene la descripción OpenAPI de un servicio?

    Imaginemos una API tan simple como la siguiente, creada con ASP.NET Core MVC:
    [Route("[controller]")]
    public class CalculatorController : ControllerBase
    {
    /// <summary>
    /// Divides two numbers
    /// </summary>
    /// <param name="a">The dividend</param>
    /// <param name="b">The divisor</param>
    /// <response code="200">Result of dividing "a" by "b"</response>
    /// <response code="400">It's not possible to divide by zero</response>
    [HttpGet("[action]/{a}/{b}")]
    [Produces("application/json")]
    [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public ActionResult<int> Divide(int a, int b)
    {
    if (b == 0)
    {
    return BadRequest();
    }
    return a / b;
    }
    }
    La descripción o esquema OpenAPI de este servicio en formato JSON podría ser la siguiente (he añadido algunos comentarios para explicar los principales puntos):
    {
    "openapi": "3.0.1",
    "info": {
    "title": "Calculator",
    "version": "v1"
    },
    "paths": {
    "/Calculator/Divide/{a}/{b}": { // <-- Ruta del endpoint
    "get": { // <-- Verbo HTTP
    "tags": [
    "Calculator"
    ],
    "summary": "Divides two numbers",
    "parameters": [
    {
    "name": "a", // <-- Parámetro "a"
    "in": "path",
    "description": "The dividend",
    "required": true,
    "schema": {
    "type": "integer",
    "format": "int32"
    }
    },
    {
    "name": "b", // <-- Parámetro "b"
    "in": "path",
    "description": "The divisor",
    "required": true,
    "schema": {
    "type": "integer",
    "format": "int32"
    }
    }
    ],
    "responses": {
    "200": { // <-- Retorno 200 OK
    "description": "The result of dividing \"a\" by \"b\"",
    "content": {
    "application/json": {
    "schema": {
    "type": "integer",
    "format": "int32"
    }
    }
    }
    },
    "400": { // <-- Retorno 400 Bad request
    "description": "It's not possible to divide by zero"
    }
    }
    }
    }
    }
    }
    Sin duda algo verboso, pero no tenemos que preocuparnos por ello porque vamos a ver algo más adelante que un componente llamado Swashbuckle puede generar esta descripción por nosotros :)

    Lo importante es que el contenido es bastante claro y que el servicio queda especificado por completo, al más mínimo detalle.

    Describiendo nuestras APIs HTTP

    El paquete NuGet Swashbuckle.AspNetCore proporciona componentes para generar de forma automática la descripción OpenAPI de servicios creados con ASP.NET Core. Obviamente tendremos que ayudarlo un poco, como habéis visto en el servicio anterior, por ejemplo introduciendo comentarios o indicando expresamente los tipos de resultado, porque de otra forma este componente no podría aportar tanta información a los clientes.

    Pero la ventaja es que esto lo haremos a nivel de código, e incluso disponemos de analizadores estáticos de Open API que nos ayudarán a completar la información que necesitemos para lograr una documentación de calidad.

    Para generar automáticamente la documentación de nuestros API, lo primero que debemos hacer es instalar el paquete Swashbuckle.AspNetCore y, tras ello, registrar sus servicios en el inyector de dependencias de ASP.NET Core:
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
    c.SwaggerDoc("api", new OpenApiInfo { Title = "Awesomic calculator", Version = "v1" });
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
    });
    ...
    }
    En la llamada a SwaggerDoc() establecemos los parámetros esenciales: el nombre que vamos a dar al documento generado, y luego alguna metainformación básica sobre el API, como su nombre descriptivo o versión (ambos aparecerán en la descripción OpenAPI).

    A continuación, indicamos al componente dónde puede encontrar el archivo XML con la documentación de clases y métodos, obteniendo primero la ruta correcta y pasándosela al método IncludeXmlComments().

    Podemos activar la generación de documentación XML desde las propiedades del proyecto, o bien introduciendo el siguiente bloque en el archivo .csproj (fijaos que el <NoWarn> es sólo para que el sistema no nos avise en otras clases que tengamos sin documentar):
    <PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);1591</NoWarn>
    </PropertyGroup>
    Tras el registro de servicios, ya sólo tenemos que añadir al pipeline el middleware que se encargará de retornar la descripción del API:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    ...
    app.UseSwagger();
    ...
    }
    ¡Y esto es todo! Usando la configuración por defecto, una petición a la ruta /swagger/api/swagger.json retornará el esquema de la API según la especificación OpenAPI. Fijaos que ese "api" de la ruta debe corresponder con el nombre que dimos al documento cuando registramos el servicio anteriormente:

    Especificación OpenAPI de nuestro servicio

    A partir de este momento,ya podremos comenzar a utilizar herramientas que se alimenten de esta definición como Swagger UI o generadores de código, pero, para no extendernos más, lo dejaremos para otro post más adelante :)

    Publicado en Variable not found.

    » Leer más, comentarios, etc...

    Variable not found

    ¿Qué es Blazor, eso de lo que todo el mundo habla?

    enero 27, 2020 05:03

    Blazor Recuerdo que la primera vez que, en una de mis visitas a Redmond, vi a Steve Sanderson haciendo una demo de Blazor "en vivo" me pareció casi magia: una aplicación web cuya lógica de presentación y UI se implementaba exclusivamente en C#, eliminando por completo de la ecuación a Javascript. ¡Uau!

    En aquellos tiempos era aún poco más que un prototipo y no se podía saber si aquella línea de investigación llevaría a alguna parte, pero era tan prometedor que, algún tiempo después, a primeros de 2018, Blazor pasó a ser un proyecto experimental del equipo ASP.NET de Microsoft.

    A partir de ese momento se fueron despejando las incógnitas que quedaban en cuanto a la viabilidad real de convertir aquello en un producto y se continuaron implementando funcionalidades, hasta que, en abril de 2019, Daniel Roth anunció que daban por concluida la fase experimental y Blazor entraba oficialmente a formar parte del stack de tecnologías para la web.

    A día de hoy, Blazor es un framework completo para el desarrollo de aplicaciones web SPA, como lo pueden ser Angular, React o Vue. Dispone de sistema de bindings, routing, componentes, ciclo de vida, validaciones, plantillas, gestión de errores, inyección de dependencias, etc. Todo lo que podemos necesitar para crear aplicaciones web profesionales de calidad.

    La primera versión de Blazor se lanzó acompañando a .NET Core 3.0 en Septiembre de 2019, aunque sólo en uno de sus modelos de hospedado: el denominado server-side o Blazor Server.

    Pero no era ese el único plan...

    Los "sabores" de Blazor: client-side, server-side... y otros

    Inicialmente, la idea de Blazor era conseguir ejecutar el código C# en el browser llevando a éste una versión deducida del runtime de .NET. Este enfoque suele conocerse como Blazor WebAssembly o client-side, pues se basa en llevar físicamente al browser la ejecución de la parte cliente de la aplicación web.

    WebAssemblyLa clave del funcionamiento de Blazor client-side es un runtime de .NET que corre sobre WebAssembly (WASM), una especificación abierta que define un "lenguaje ensamblador" común a todas las máquinas virtuales presentes en los navegadores modernos (y pollyfillable para los anteriores). Sobre ese runtime, ya en el lado cliente, es donde corren nuestras aplicaciones Blazor cuando elegimos este modelo de hospedaje.

    Así, la primera vez que un usuario accede a una aplicación Blazor WebAssembly, su navegador descarga tanto el runtime de .NET como los ensamblados propios de la aplicación (compilados desde C# en tiempo de desarrollo) y sus dependencias. Ya tras esta descarga inicial, que gracias al caché de los navegadores se realizaría sólo una vez, los ensamblados de la aplicación serían ejecutados por el runtime directamente desde el navegador.

    Un detalle importante a tener en cuenta es que el hecho de que el código cliente de nuestras aplicaciones lo desarrollemos usando .NET no significa que estemos llevando al cliente todo el potencial de esta plataforma. Dado que este código se ejecuta en cliente, al igual que cuando programamos con Javascript, estamos limitados por el sandbox proporcionado por el navegador para la ejecución segura de aplicaciones. Es decir, no podremos acceder a dispositivos o recursos locales más allá de los permitidos por las APIs estándar de los navegadores.

    Por otra parte, como la ejecución se realiza puramente en cliente, no sería estrictamente necesario disponer de un servidor ASP.NET, IIS ni nada parecido. Tanto el runtime como los ensamblados resultantes de la compilación podrían ser distribuidos en un CDN, y es lo único que se necesitaría para poner en marcha la aplicación (bueno, a no ser que ésta necesite de un backend, claro.)

    ¿Y qué ventajas tiene esto? Pues bueno, ya el simple hecho de sustituir Javascript por C# para implementar el lado cliente puede aportarnos ventajas interesantes. Aparte de que C# es un lenguaje muy superior, también será posible utilizar o reutilizar bibliotecas .NET existentes, compartir código entre cliente y servidor, o utilizar entornos tan potentes como Visual Studio durante la fase de desarrollo.

    Pero como siempre ocurre, no es la panacea; también provocará que la carga inicial de la aplicación sea más lenta (aunque, como decíamos, la caché podrá ayudarnos con esto), o requerirá que los clientes sean compatibles con WASM (aunque esto ya no es un problema en la mayoría de los casos).
    Y bueno, de alguna forma nos alejaremos del ecosistema Javascript, que ya sabemos que es enorme, aunque también es cierto que desde Blazor es posible interoperar con bibliotecas JS existentes.
    En la actualidad, Blazor WebAssembly sólo está disponible en preview, y está previsto lanzar la primera versión oficial en mayo de 2020 (aunque ésta aún no será LTS).
    Como comentaba algo más arriba, el enfoque client-side de Blazor fue el origen del proyecto y sigue siendo el objetivo principal del mismo. Sin embargo, durante la fase de desarrollo aparecieron otros escenarios de uso de Blazor que podrían resultar interesantes y entraron a formar parte del Roadmap del producto, modificando los planes iniciales:

    Blazor Roadmap

    En la ilustración anterior podemos observar que el Roadmap posicionó Blazor Server como el primer hito del conjunto de tecnologías Blazor. De hecho, es el único framework del que tenemos a día de hoy una versión estable, production ready y con soporte a largo plazo.

    Blazor Server parte del mismo objetivo que Blazor WebAssembly respecto a utilizar C# para la implementación de la lógica de presentación, aunque el enfoque que sigue es bastante diferente. En este caso no se trata de llevar todo al cliente sino al contrario: hacer que todo se ejecute en el servidor, comunicándose con el browser mediante una conexión persistente de SignalR:

    Blazor WebAssembly vs Blazor Server

    Para conseguirlo, el servidor mantiene en memoria una representación del estado de la página, que es modificada por mensajes SignalR que son enviados cuando se producen cambios o interacciones en la interfaz de usuario.

    Por ejemplo, si un usuario pulsa un botón en la página, el evento "click" no es procesado en cliente sino enviado a través de la conexión SignalR al servidor, que ejecutará el handler oportuno (escrito en C#). Los cambios realizados en el UI desde dicho handler son enviados de vuelta al cliente, que actualizará el DOM de forma automática.
    Pues sí, en cierto modo es muy similar a lo que utilizábamos en Web Forms años atrás, pero eliminando la pesadez del postback, la carga de página completa y el mantenimiento del estado mediante el célebre viewstate. De hecho, Blazor Server es la tecnología propuesta por Microsoft para sustituir a Web Forms, al promover un tipo de desarrollo conceptualmente muy parecido.
    Como siempre ocurre, la idea tiene sus ventajas e inconvenientes. Como parte positiva respecto al modelo client-side, en este caso la carga inicial sería más rápida porque no hay nada que descargar desde el cliente. También, dado que el código se ejecutaría en el servidor, podríamos utilizar todo tipo de bibliotecas y componentes para .NET, así como las herramientas habituales de desarrollo y depuración. Y para casos de necesidad, este enfoque conseguiría casi el soporte universal por parte de los browsers, pues no requiere ninguna característica especial en el lado cliente.

    Pero ah, amigos, todo esto no es gratis. El hecho de que cada interacción o evento en el UI deba ser enviado y procesado en el servidor añade una latencia que no todas las aplicaciones podrán soportar sin destrozar su usabilidad. Además, las conexiones concurrentes y el mantenimiento del estado en memoria podrían limitar las opciones de escalabilidad de los sistemas.

    La buena noticia es que el modelo de componentes, y por tanto la forma de desarrollar, es la misma. De esta forma, podríamos iniciar un proyecto utilizando Blazor Server y migrar a Blazor WebAssembly más adelante con relativamente poco esfuerzo. Bueno, o al menos en teoría ;)

    Pero aparte de Blazor Server y Blazor WebAssemby, y como veíamos en el Roadmap algo más arriba, el resto de tecnologías Blazor van acercándose consecutivamente a la ejecución nativa de aplicaciones, tanto desde dispositivos móviles como de escritorio:
    • Blazor PWA, para la construcción de Progressive Web Applications (PWA), webs que proporcionan una experiencia más cercana a las aplicaciones nativas al ser capaces de trabajar offline, recibir notificaciones push, o ser instaladas en los equipos de los usuarios. Sin embargo, la ejecución se realizará siempre sobre el sandbox proporcionado por el browser, por lo que tendremos las mismas limitaciones que si utilizáramos JS.
       
    • Blazor Hybrid, aplicaciones que correrán sobre Electron y renderizarán su UI utilizando tecnologías web sobre WebViews o motores de renderizado de webs. Dado que serán aplicaciones .NET puras, en este caso sí se podrá acceder a todos los recursos del equipo donde se instalen.
       
    • Blazor Native, que permitirá en el futuro utilizar el mismo modelo de programación para crear aplicaciones nativas puras, capaces de renderizarse utilizando componentes nativos de la plataforma de ejecución, fuera del mundo web. En la práctica, quiere decir que podríamos utilizar esta tecnología en lugar de Xamarin o React Native para implementar nuestras aplicaciones nativas.
    Aunque muy sujeto a cambios, pero está previsto lanzar previews de PWA e Hybrid a finales de 2020, acompañando a .NET 5. La última referencia que he encontrado de Blazor Native es que está en fase experimental y, por tanto, no existen aún previsiones de disponibilidad.

    Solución usando Mobile Blazor BindingsAparte, hace apenas un par de días han anunciado otro giro de tuerca: Mobile Blazor Binding. Este proyecto, aún en fase experimental, permite utilizar el modelo de programación de Blazor y la sintaxis Razor para la construcción de aplicaciones nativas Android e iOS, en la práctica algo así como un híbrido entre Blazor y Xamarin. El objetivo es acercar al mundo de las apps nativas a esa gran masa de desarrolladores web acostumbrados a Razor, y la verdad es que tiene bastante buena pinta.

    Esto promete, ¿eh? 😉

    ¿Cómo se desarrolla con Blazor?

    Por si no os lo habéis preguntado, el nombre Blazor procede de la unión de "Browser" y "Razor" (la "L" no sé de dónde sale, supongo que dará más musicalidad al resultado :-P), así que ya podéis intuir por dónde van los tiros: Razor que se ejecuta en el navegador.

    De hecho, los componentes y páginas Blazor se implementan utilizando sintaxis Razor, que muchos ya conocéis de otras películas como ASP.NET Core MVC o Razor Pages, aunque con algunos cambios. Por ejemplo, en Blazor las páginas o componentes se implementan en archivos .razor, a diferencia de los clásicos .cshtml de MVC o Razor Pages.

    Pero como las cosas se suelen entender mejor con código, mejor que echéis primero un vistazo al siguiente archivo Sum.razor:
    @page "/sum"

    <h1>Calculator</h1>
    <div>
    <input type="number" @bind-value="@A" />
    +
    <input type="number" @bind-value="@B" />
    <button @onclick="Calculate">Calculate</button>
    </div>
    @if (Result != null)
    {
    <div>
    The sum of @A and @B is @Result
    </div>
    }

    @code {
    public int? A { get; set; }
    public int? B { get; set; }
    public int? Result { get; set; }

    void Calculate()
    {
    Result = A + B;
    }
    }
    Calculadora BlazorEchando un vistazo puede entenderse que se trata de una simple calculadora para realizar sumas que incluye un par de tags <input> para solicitar los sumandos y un botón para realizar el cálculo. Al pulsarlo, se muestra el resultado de la suma en un <div> que inicialmente no está visible.

    Este bloque de código es suficiente para entender la magia de Blazor:
    • En el bloque @code, implementado por completo en C#, definimos todas las propiedades y métodos que necesitemos para implementar la lógica de nuestra página. En este caso, tenemos un par de propiedades para los sumandos, otra para el resultado, y un método que realiza el cálculo.
      @code {
      public int? A { get; set; }
      public int? B { get; set; }
      public int? Result { get; set; }

      void Calculate()
      {
      Result = A + B;
      }
      }
    • Utilizamos bindings sobre los controles HTML, muy al estilo de otros frameworks basados en MVVM, como Angular o React. En el ejemplo podemos ver que bindeamos el valor de los <input> a las propiedades definidas en el bloque @code, de la misma forma que establecemos el manejador del evento click al método Calculate().
      <input type="number" @bind-value="@A" />
      +
      <input type="number" @bind-value="@B" />
      <button @onclick="Calculate">Calculate</button>
    • Dado que usamos sintaxis Razor, es posible utilizar los habituales bloques de control de flujo como @if, @foreach, etc. En el caso anterior, usamos un condicional para añadir a la página el <div> con el resultado, pero sólo cuando hemos calculado la suma.
      @if (Result != null)
      {
      <div>
      The sum of @A and @B is @Result
      </div>
      }
    Fijaos que hemos eliminado de un plumazo a Javascript, y la web funcionará sin cambiar de página, muy al estilo SPA:
    • Si hemos optado por utilizar el host Blazor Server, todos los eventos e interacciones sobre el UI (cambios de valores en controles bindeados, clicks, etc.) serán enviados al servidor a través de la conexión SignalR establecida automáticamente. El servidor ejecutará el código y enviará de vuelta al cliente, usando la misma conexión, las modificaciones a realizar en el DOM para actualizar la interfaz de usuario.

    • En cambio, si hemos optado por utilizar Blazor WebAssembly, la página será compilada y el ensamblado resultante será enviado al cliente, quien lo ejecutará sobre el runtime basado en WebAssembly. Por tanto, la ejecución de la página se realizará totalmente en cliente, como si la hubiéramos implementado totalmente Javascript, sin necesidad de interactuar con el servidor en ningún momento.
    Y lo interesante es que en ambos casos nuestro código será el mismo. Es decir, la página Sum.razor podrá ser (en teoría) la misma para cualquiera de los modelos de ejecución de Blazor, por lo que podríamos crear nuestras aplicaciones e ir evolucionando posteriormente de un modelo a otro. Por ejemplo, a día de hoy sólo podríamos poner aplicaciones Blazor en producción usando Blazor Server, pero en unos meses podríamos pasar al modelo WebAssembly cuando esté disponible, realizando pocos cambios.

    Pero no sólo eso, la idea también aplica para el resto de "sabores" de Blazor, como PWA, Hybrid o Native: lo único que cambiará es el "empaquetado" y, en algunos casos, el proceso interno de renderizado, pero nuestro código seguirá siendo principalmente el mismo... o al menos, estas son las intenciones iniciales; conforme avancen estos proyectos iremos viendo hasta qué punto es así.

    En definitiva, Blazor es un framework del que seguro vamos a oír hablar bastante durante los próximos meses, y no sin razón. Aunque aún está empezando a rodar y quedan bastantes cosas por pulir, es conveniente no perderlo de vista porque realmente las expectativas están muy altas :)

    Publicado en Variable not found.

    » Leer más, comentarios, etc...

    Navegapolis

    La empresa consciente

    enero 19, 2020 06:07

    empresa pinAlgunas perlas del libro La empresa consciente de Fredy Kofman.

     

    "Yo utilizo siete cualidades para distinguir a los empleados conscientes de los inconscientes.

    Las primeras tres son atributos de la personalidad: responsabilidad incondicional, integridad esencial y humildad ontológica. Las tres siguientes son habilidades interpersonales: comunicación auténtica, negociación constructiva y coordinación impecable. La séptima cualidad es la condición que hace posibles a las seis anteriores: maestría emocional.

    Comprender en qué consisten estas cualidades es algo sencillo, pero es difícil ponerlas en práctica. Parecen naturales, pero desafían supuestos profundamente arraigados acerca de nosotros mismos, de otras personas y del mundo. Por este motivo, aun cuando generalmente las conocemos, no sabemos cómo implementarlas. Son cuestión de sentido común, pero no de práctica común.

    Los empleados conscientes asumen responsablemente su vida. No comprometen valores humanos para lograr el éxito material. Dicen su verdad y escuchan las verdades de los demás con honestidad y respeto. Buscan soluciones creativas a los desacuerdos y honran de manera impecable sus responsabilidades. Se conectan con sus emociones y las expresan productivamente.

    Los empleados inconscientes hacen lo opuesto. Culpan a otros por sus problemas, buscan gratificación inmediata olvidando la ética, y proclaman tener siempre la razón."

     

    » Leer más, comentarios, etc...

    Coding Potions

    Vue JS ≫ Cómo crear y emitir eventos entre componentes

    enero 17, 2020 12:00

    Introducción

    Anteriormente vimos qué son los props en Vue y para qué sirven. Vimos que los props sirven para pasar información desde el componente padre al componente hijo. Si no recuerdas o quieres aprender el funcionamiento de los props te dejo el enlace al artículo: Cómo crear y usar props en Vue

    En el artículo de hoy vamos a ver cómo crear eventos y para qué sirven ya que son esenciales para la creación de componentes web reutilizables.

    Qué son los eventos en Vue

    Los eventos o events son la contraparte de los props, es decir, sirven para pasar información desde el componete hijo al padre. El ejemplo típico de uso es de cuando queremos notificar de un suceso en el componente hijo.

    Por ejemplo podemos crear un componente que simplemente renderice un botón. Si queremos que este botón sea reutilizable en toda la página web lo que necesitamos es notificar al componente que usa el botón si el usuario hace clic en el enlace.

    Lo bueno de los eventos es que a parte de servir para notificar, se pueden pasar datos e información desde el hijo al padre en caso de que la necesitemos.

    Los props se pasan desde el componente padre al componente hijo y los eventos del hijo al padre

    Cómo crear y emitir eventos

    Aunque no lo creas, si has usado Vue seguramente ya hayas usado sin saberlo los eventos ya que el @submit y el @click no son más que eventos que ya vienen por defecto en Vue.

    Como supondrás, la forma de recibir eventos es poniendo el @ antes del nombre del evento, pero vamos a ver cómo crear tus propios eventos desde el componente hijo.

    Volvamos al ejemplo del Botón ya que es un ejemplo claro para usar eventos. La estrategia a seguir es reaccionar con el evento click de Vue para ejecutar una función que sera la encargada de enviar el evento al componente padre.

    <template>
      <button @click="handleClick">{{text}}</button>
    </template>
    <script>
    export default {
      props: {
        text: String
      },
      methods: {
        handleClick() {
          this.$emit("click");
        }
      }
    }
    </script>
    

    Con la función this.$emit() emites el evento. Dentro de la función tienes que poner el nombre que le quieres dar al evento, es decir, si por ejemplo pones this.$emit("hover"); luego para usarlo tienes que llamar al evento @hover. En este ejmplo le he puesto el nombre de click para que podamos estar a la escucha del click en el botón como si se tratase del evento click que viene en Vue por defecto.

    Hay otra forma de crear eventos, se trata de crearlos directamente desde la vista, sin tener que poner el this:

    <template>
      <button @click="$emit('click')">{{text}}</button>
    </template>
    

    De esta forma ya no necesitas crear un método desde el que enviar el evento. Con ambas formas obtienes el mismo resultado.

    Cómo estar a la escucha de eventos

    Para usar el botón que acabamos de crear:

    <my-button text="Botón de ejemplo" @click="handleClick"></my-button>
    

    Recuerda importarlo dentro del componente para poder usarlo. Si hubieras llamado al evento hover por ejemplo, entonces sería:

    <my-button text="Botón de ejemplo" @hover="handleClick"></my-button>
    

    Cómo enviar datos en los eventos

    Una cosa muy interesante de Vue es que permite enviar información dentro de los eventos. Puedes enviar cualquier cosa: variables, computadas, resultados de métodos, etc.

    Para enviar datos tan solo tienes que añadir un parámetro más al evento:

    <template>
      <button @click="handleClick">{{text}}</button>
    </template>
    
    <script>
    export default {
      props: {
        text: String
      },
      data: () => ({
        example: "Hello world"
      }),
      methods: {
        handleClick() {
          this.$emit("click", this.example);
        }
      }
    }
    </script>
    

    Para recoger en el componente padre la información que viene del hijo lo tienes que hacer en el método que declaras cuando declaras el evento que estás escuchando:

    <template>
      <my-button text="Botón de ejemplo" @click="handleClick"></my-button>
    </template>
    
    <script>
    import MyButton from "@/components/MyButton.vue";
    
    export default {
      components: {
        MyButton
      },
      methods: {
        handleClick(info) {
          console.log("Click event on the button of the children with: " + info)
        }
      }
    }
    </script>
    

    Si te fijas, en la vista HTML solo necesitas llamar al nombre del método, en otras palabras NO tienes que hacer esto:

    <my-button text="Botón de ejemplo" @click="handleClick(info)"></my-button>
    

    Conclusiones

    Este artículo es muy corto porque los eventos no dan para más. Es algo muy simple de implementar y encima muy útil para la creación de los componentes si lo combinas con los props.

    La única desventaja de los props y los eventos es que si por ejemplo tienes herencias más completas (hijos con más componentes hijos), tienes que pasar los props y los eventos hacia arriba y hacia abajo en la herencia, aunque ya veremos como solucionar esto con el store de Vue.

    » Leer más, comentarios, etc...

    Picando Código

    Nuevo diseño en Montevideo Bicis 🚲

    enero 15, 2020 11:15

    Lo que empezó con una corrección mínima de un enlace en Montevideo Bicis me llevó a hacer algunos cambios bastante grandes:

    • Migré de Bootstrap a Bulma.io, mi nuevo framework CSS preferido
      Lo que también elimina la dependencia del JavaScript completo de Bootsrap. Cambió bastante el diseño, dándole una apariencia un poco más moderna (en mi muy humilde opinión). También me restula más fácil de mantener. Como vengo usando Bulma en varios proyectos, lo tengo más fresco en la memoria para cambiar cosas.
    • Eliminé la dependencia jQuery 🙌
      En su momento fu un sitio hecho lo más rápido posible para publicarse enseguida. Y la dependencia con jQuery vino con la facilidad de desarrollo. Pero en verdad hay tan poco JavaScript en el sitio que no valía la pena incluirlo. Y hoy en día no es muy necesario, menos para sitios tan pequeños como éste.
    • Quité la imagen de fondo
      No quedaba demasiado bien de todas formas, y es un request menos en el navegador.

    Todavía puede quedar alguna cosa por corregir, pero en principio creo que se ve un poco mejor, y sin duda quedó más liviano al no cargar tanto JavaScript y CSS innecesarios.

    Cómo se veía antes:

    Montevideo Bicis Antes

    Cómo se ve ahora: MontevideoBicis.com
    Si llegan a encontrar algún error o detalle, pueden dar de alta un issue en GitHub o contactarme por acá o por Twitter.

    » Leer más, comentarios, etc...

    Meta-Info

    ¿Que es?

    Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

    rss subscripción

    Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
    planetacodigo

    planetacodigo

    Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

    Idea: Juanjo Navarro

    Diseño: Albin