Weblogs Código

Bitácora de Javier Gutiérrez Chamorro (Guti)

RAD Studio Community Edition (gratis)

julio 19, 2018 06:10

Os traigo un anuncio muy interesante, puesto que como muchos sugeríamos y deseábamos, finalmente Embarcadero ha decidido liberar una versión gratuita y verdaderamente funcional de RAD Studio, es decir, tanto de C++ Builder como de Delphi. Se trata de RAD Studio Community Edition, o dicho de otro modo, de Delphi Community Edition y C++ Builder […]

La entrada RAD Studio Community Edition (gratis) aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

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

Picando Código

Humble Book Bundle: Linux Geek

julio 18, 2018 09:48

Humble Bundle tiene un nuevo paquete de e-books sobre Linux , GDB, Perl, GIMP, Inkscape, Blender, Python, y más de No Starch Press.

Linux Geek Book Bundle

Pagando el monto mínimo de USD 1, obtenemos: Automate the Boring Stuff with Python: Practical Programming for Total Beginners, The Artist’s Guide to GIMP: Creative Techniques for Photographers, Artists, and Designers, The Art of Debugging with GDB, DDD, and Eclipse, Perl One-Liners: 130 Programs That Get Things Done, The Book of GNS3: Build Virtual Network Labs Using Cisco, Juniper, and More y No Starch Sampler. Éste último un compilado de capítulos de muestra de varios libros sobre Linux, redes y seguridad.

Pagando USD 8 o más, también obtenemos: The Book of Inkscape: The Definitive Guide to the Free Graphics Editor, The Book of GIMP: A Complete Guide to Nearly Everything, The Book of PF: A No-Nonsense Guide to the OpenBSD Firewall, The GNU Make Book, Blender Master Class: A Hands-On Guide to Modeling, Sculpting, Materials, and Rendering y Doing Math with Python: Use Programming to Explore Algebra, Statistics, Calculus, and More!

Pagando USD 15 o más, el paquete incluye: How Linux Works: What Every Superuser Should Know, Wicked Cool Shell Scripts: 101 Scripts for Linux, OS X, and UNIX Systems, Absolute OpenBSD: Unix for the Practical Paranoid, Arduino Project Handbook Vol. 1: 25 Practical Projects to Get You Started y Think Like a Programmer: An Introduction to Creative Problem Solving.

Por último, pagando USD 30 o más, obtenemos The Linux Programming Interface: A Linux and UNIX System Programming Handbook.

Los ebooks están disponibles en PDF, ePUB y MOBI, por lo que pueden ser leídos en casi cualquier dispositivo. Como se acostumbra en los paquetes Humble Bundle, además de elegir el precio, podemos elegir dónde destinamos el dinero que pagamos, incluyendo una organización de beneficencia.
En este caso se trata de Electronic Frontier Foundation, la fundación sin fines de lucro que defiende nuestros derechos en el mundo digital. Activistas, abogados y tecnólogos que luchan en las cortes y el congreso para proteger la privacidad y libre expresión en línea en todo el mundo.

Visita el Linux Geek Book Bundle.

Linux Geek Book Bundle

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

Adrianistán

Computación cuántica para torpes: introducción para programadores

julio 17, 2018 01:57

Hace ya bastante tiempo quese viene hablando de ordenadores cuánticos. ¿Qué son? ¿En qué se diferencian de los ordenadores clásicos? ¿Son de verdad tan potente como dicen?

En este largo artículo, ideal para el verano, vamos a ver los principios fundamentales de los ordenadores cuánticos más allá de lo que la típica revista o web te podría contar. Veremos qué es un qubit y algunas puertas lógicas interesantes, así como su aplicación.

Los ordenadores cuánticos actuales requieren temperaturas de funcionamiento cercanas al cero absoluto

Notación de Dirac

Lo primero antes de entrar en materia cuántica, será adaptar nuestra notación clásica, a otra más potente. Esta notación que vamos a ver, llamada notación de Dirac o Bra-ket, nos será muy útil ya que los bits clásicos no son más que un caso concreto de qubit en esta notación.

En esta notación tenemos que representar los bits como matrices. Un conjunto de N bits se representa con una matriz de 1 columna y \(2^N\) filas. En todas las posiciones existen ceros salvo para la posición que representa la combinación que representa. Veamos algunos ejemplos sencillos:

Un bit con valor 0 se representa así

\(
| 0 \rangle = \begin{pmatrix}
1\\
0
\end{pmatrix}\)

Un bit con valor 1 se representa así:

\(
| 1 \rangle = \begin{pmatrix}
0\\
1
\end{pmatrix}\)

Aquí contamos como en informática, empezando desde cero. Como ves la posición 0 del vector solo es 1 cuando representa el bit 0. Si la posición que tiene el 1 es la segunda, representa el bit 1.

La parte que va a la izquierda del igual se llama ket. En este caso hemos representado ket 0 y ket 1.

Si tenemos más bits se puede hacer de la misma forma. Vamos a representtar ket 10. 10 en binario es 2, así que estará en la posición tercera.

\(
| 10 \rangle = \begin{pmatrix}
0\\
0\\
1\\
0
\end{pmatrix}\)

Puertas lógicas como producto de matrices

¿Recuerdas el producto de matrices de tus clases de álgebra? Resulta que todas las puertas lógicas clásicas pueden representarse como producto de matrices. Por ejemplo, la puerta lógica NOT se puede implementar con esta matriz:

\(
\begin{pmatrix}
0 & 1 \\
1 & 0 \\
\end{pmatrix}
\)

Y aquí la vemos en acción

\(
\begin{pmatrix}
0 & 1 \\
1 & 0 \\
\end{pmatrix}\begin{pmatrix}
1 \\
0
\end{pmatrix}
=
\begin{pmatrix}
0 \\
1
\end{pmatrix}
\)

También podría hacerse con la puerta AND que toma como entrada dos bits.

\(
\begin{pmatrix}
1 & 1 & 1 & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
0 \\
0 \\
0 \\
1
\end{pmatrix}
=
\begin{pmatrix}
0 \\
1
\end{pmatrix}
\)

Un teclado con puertas cuánticas

Juntando bits

Para formar bits más grandes ya sabemos que tenemos que tener una matriz tan grande como combinaciones haya (\(2^N\) posiciones, N bits). Existe una forma de calcular automáticamente la posición que hay que marcar y es hacer el producto tensorial. Si no sabes calcularlo no importa mucho, porque apenas lo vamos a usar, pero puedes ver un ejemplo de como funciona. En este ejemplo, queremos juntar los bits 1 y 1 (3 en decimal).

\(
\begin{pmatrix}
0 \\
1
\end{pmatrix}
\otimes
\begin{pmatrix}
0 \\
1
\end{pmatrix}
=
\begin{pmatrix}
0 \\
0 \\
0 \\
1
\end{pmatrix}
\)

Qubits

Hasta ahora no hemos visto nada realmente nuevo, solo hemos preparado el terreno para dejar paso a los qubits. Personalmente desconocía que podían usarse matrices para operar con bits y aunque no es tan práctico como otros sistemas, lo cierto es que es muy explícito y elegante.

Los qubits son bits como los que hemos visto antes pero tienen un estado indeterminado. No es que tengan un valor de forma secreta y no sepamos cuál es. Podríamos decir que son los dos valores clásicos a la vez, como el gato de Schrodinger. Cuando realizamos una observación sobre el qubit, este colapsa y podemos ver uno de los dos estados. ¿Cuál vemos? No podemos saberlo a priori, pero hay probabilidades. Los bits clásicos no son más que qubits cuyas probabilidades de colapsar a 0 o 1 es del 100%, por tanto no hay duda y su valor sí que está determinado.

¿Cómo representamos los qubits y sus estados cuánticos? Con números complejos. Si recuerdas, estos eran los que tenían una parte real y una imaginaria. No obstante, voy a tomar solo los números reales para simplificar. Los números reales son números complejos válidos, pero los complejos son mucho más extensos.

La esfera de Bloch permite representar todos los estados cuánticos (números complejos)

Para calcular la probabilidad que tiene un estado cuántico de colapsar a un valor, hacemos el módulo y elevamos al cuadrado: \(|a|^2\).

Todo qubit además debe cumplir una propiedad fundamental:

\(
\begin{pmatrix}
a \\
b
\end{pmatrix}
\text{ es un qubit}
\Leftrightarrow
|a|^2 + |b|^2 = 1
\)

Y es que la probabilidad de ser 0 y de ser 1 sumadas deben equivaler al suceso seguro, es decir, 1. 100% de probabilidades de que de 0 o 1.

Con esto ya podemos definir algunos qubits.

\(
\begin{pmatrix}
\frac{1}{\sqrt{2}} \\
\frac{1}{\sqrt{2}}
\end{pmatrix}
\)

Este es mi qubit preferido. Representa el estado de superposición cuántica. Cada valor tiene el 50% de probabilidades de salir. \(|\frac{1}{\sqrt{2}}|^2 = \frac{1}{2}\). Cuando colapsemos el qubit al observarlo será como lanzar una moneda al aire.

Otro detalle que a veces se nos pasa por alto es que los qubits pueden contener valores negativos. Estos qubits son físicamente diferentes a los positivos, pero tienen las mismas probabilidades de colapsar en los mismos valores que los positivos.

\(
\begin{pmatrix}
-1 \\
0
\end{pmatrix}
\)

Es un qubit válido, que colapsa con 100% de probilidad a 0.

¿Cómo se representan los qubits en notación de Dirac? Representando la probabilidad que tiene cada combinación de bits de aparecer. Para un qubit sería algo así:

\(
\alpha | 0 \rangle + \beta | 1 \rangle
\)

Siendo \(\alpha\) y \(\beta\) las probabilidades de colapsar a cada estado.

Puertas cuánticas

Ahora vamos a ver cuáles son las puertas lógicas más importantes del mundo cuántico.

Negación (Pauli X)

Esta es exactamente igual que en el mundo clásico, con la misma matriz que hemos visto antes. Su símbolo es el cuadrado con una X.

Aquí vemos una imagen del simulador IBM Q usando la puerta X cuántica. IBM nos deja ejecutarlo en ordenadores cuánticos reales. Veamos los resultados.

¡Terrible! La mayoría de casos, el ordenador cuántico responde 1, el valor correcto, pero un 13% de los casos no. Teóricamente había una probabilidad del 100% y en la práctica solo es del 86.3%. ¡Y solo es una puerta X! Es por ello que los procesadores cuánticos todavía necesitan mejorar mucho. Google, Microsoft e IBM están investigando de forma independiente en ordenadores cuánticos. Veremos quién consigue tener antes ordenadores cuánticos precisos (aunque hay expertos que piensan que nunca se podrá lograr).

CNOT

Esta puerta es muy interesante. Toma dos qubits, uno de control, que permanece invariable al traspasar la puerta y otro de datos. Al qubit de datos se le aplica la puerta X si el qubit de control está activo. Su matriz es la siguiente:

\(
CNOT = \begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 0 & 1 \\
0 & 0 & 1 & 0
\end{pmatrix}
\)

Se reprensenta de esta forma:

O similar, porque los símbolos de computación cuántica no están todavía muy estandarizados. El punto grande es el qubit de control y el punto pequeño está sobre el qubit de datos.

HADAMARD

Esta puerta es quizá la más famosa del mundo cuántico. Veamos su matriz:

\(
\begin{pmatrix}
\frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \\
\frac{1}{\sqrt{2}} & -\frac{1}{\sqrt{2}}
\end{pmatrix}
\)

Esta puerta permite poner un qubit clásico en estado de superposición cuántica. Y también deshacerlo. Es muy usada en algoritmos cuánticos. Se representa con un cuadrado y una H.

Los resultados en el ordenador cuántico real de IBM Q son:

Cuando debería de ser bastante más cercano a 50% en los dos valores.

Con esto ya tenemos las puertas más usadas. Básicamente con Hadamard, X y CNOT se pueden implementar casi todos los circuitos cuánticos. Solo nos faltarían las puertas que operan entran en números complejos para poder implementar todos los circuitos.

Algoritmo de Deutsch-Jozsa

El algoritmo de Deutsch-Jozsa es uno de los algoritmos cuánticos más sencillos de entender y que mejoran drásticamente el rendimiento respecto a un algoritmo clásico.

El planteamiento básico es que tenemos una caja negra que aplica una función sobre un bit. Estas funciones pueden ser: set-to-0, set-to-1 (ambas constantes), identidad (no cambiar nada) y X (ambas dinámicas) . Si queremos saber que función contiene la caja negra, ¿Cuántas veces tenemos que pasar valores? En una CPU clásica tendríamos que hacerlo dos veces para poder determinar que función contiene la caja negra. En una CPU cuántica… también. No hay diferencia. Pero, si cambiamos la pregunta a ¿de qué categoría es la función de la caja negra?, la cosa cambia. En una CPU clásica seguiríamos necesitando 2 pruebas, pero en una CPU cuántica y con un circuito por el exterior, podemos aprovechar la superposición cuántica para realizar una sola prueba y determinar si en el interior hay una función constante o dinámica.

Vamos a ver estas 4 funciones de la caja negra como son:

¿Se te ocurre como puedes crear un circuito fuera de la caja negra que con una sola prueba, ya sepa si estamos ante las funciones Set-0, Set-1 o Identidad, Negación?

El circuito es el siguiente:

Tal y como está diseñado si en q[1] medimos 0, la función es de tipo constante y si medimos 1, es de tipo dinámica. Un desarrollo matemático de los productos de matrices, como el que hay en Wikipedia, te mostrará como siempre es cierto. Este también es un ejemplo de como los ordenadores cuánticos pueden dar resultados totalmente deterministas.

Esta idea, se puede generalizar y extrapolar a otros problemas, generando una colección muy interesante de algoritmos que se ejecutan en tiempo exponencialmente menor que en una CPU clásica.

Algoritmos de Shor y de Grover

Estos dos algoritmos han sido llamados los killer apps de la computación cuántica, ya que son algoritmos que mejoran sustancialmente (uno de forma exponencial, otro de forma cuadrática) los tiempos de problemas reales.

El algoritmo de Shor fue el primero en ser descubierto, en 1994 por Peter Shor. Sirve para factorizar números (es decir, sacar los números primos que multiplicados generan el número original). Lo puede hacer en \(O((\log{n})^3)\). De este modo, los algoritmos tipo RSA que se basan en la factorización de números podrían romperse en tiempo polinómico, por lo cuál RSA ya no serviría como protección. El algoritmo de Shor no da siempre los resultados correctos, pero tiene una probabilidad de éxito superior a la de fracaso, por lo que con repetir múltiples veces la ejecución podríamos estar casi seguros del resultado.

El algoritmo de Grover fue descubierto en 1996 por Lov Grover y permite buscar en una lista no ordenada de datos en \(O(\sqrt{n})\) mientras que en una computadora clásica sería \(O(n)\).

Estos dos algoritmos sin duda marcan lo que se ha llamada la supremacía cuántica y que ocurrirá cuando los ordenadores cuánticos puedan ejecutar con suficiente precisión estos algoritmos y su utilidad en el mundo real supere a la de los algoritmos clásicos.

Entrelazamiento cuántico

Ya hemos visto las bases de los circuitos cuánticos. Ahora veamos algunas consecuencias de todo lo anterior. Cosas cuánticas que parecen hasta cierto punto fuera de la lógica. ¿Qué ocurre si tenemos varios qubits en un estado como este?

\(
\begin{pmatrix}
\frac{1}{\sqrt{2}} \\
0 \\
0 \\
\frac{1}{\sqrt{2}}
\end{pmatrix}
\)

En este estado solo puede colapsar a 00 o a 11. ¿Esto qué significa? Significa que los qubits están entrelazados entre sí, uno depende de otro, si los separamos y uno colapsa a 0, el otro colapsa a 0. Si uno colapsa a 1, el otro colapsa a 1.

Es importante destacar que los qubits pueden separarse en este estados. Los qubits alejados a millones de kilómetros siguen entrelazados y el valor al que deciden colapsar se decide de forma instantánea. Esto quiere decir que se sincronizan a una velocidad superior a la de la luz. El truco es que no se transmite información, por eso el universo lo permite, pero esto permite realizar la teletransportación cuántica.

La forma de entrelazar qubits es muy sencilla, con una puerta Hadamard y una CNOT.

IBM Q todavía tiene que mejorar, pero se aprecia claramente el entrelazamiento cuántico.

Teletransportación cuántica

La teletransportación existe, al menos entre qubits. Y es instantánea (más o menos). La teletransportación cuántica la podemos provocar usando varios qubits entrelazados. Necesitamos 3 qubits. El qubit que va a ser teletransportado, un qubit del emisor y un qubit del receptor. La idea es entrelazar el emisor con el receptor (qubit de destino) y posteriormente el qubit del emisor con el qubit que va a ser transportado.

No he sido capaz de hacer que IBM Q haga una teletransportación, así que aquí viene un esquema distinto. T es el qubit a transportar, A es el emisor y B el receptor. En este ejemplo se usa la puerta Pauli Z, cuya matriz es la indicada.

El truco de la teletransportación instantánea tiene que ver con que A y B tienen que estar entrelazados, por tanto, han tenido que ser transportados a sus respectivos lugares anteriormente a velocidad inferior a la luz.

Esto teletransporta qubits pero no hace copias. Esto es debido al Teorema de No Clonación.

Lenguajes de programación

Mientras esperamos a que los ordenadores cuánticos sean lo suficientemente estables, ya existen lenguajes de programación que podemos usar en simuladores. Quizá el más conocido sea Q# de Microsoft (funciona en Linux, tranquilos), que guarda similitudes con C#. Otro bastante usado es OpenQasm de IBM, algo más parecido a ensamblador.

namespace Quantum.Bell
{
    open Microsoft.Quantum.Primitive;
    open Microsoft.Quantum.Canon;

    operation Set (desired: Result, q1: Qubit) : ()
    {
        body
        {
            let current = M(q1);
            if (desired != current)
            {
                X(q1);
            }
        }
    }
    operation BellTest (count : Int, initial: Result) : (Int,Int,Int)
    {
        body
        {
            mutable numOnes = 0;
            mutable agree = 0;
            using (qubits = Qubit[0])
            {
                for (test in 1..count)
                {
                    Set (initial, qubits[0]);
                    Set (Zero, qubits[1]);

                    H(qubits[0]);
                    CNOT(qubits[0],qubits[1]);
                    let res = M (qubits[0]);

                    if (M (qubits[1]) == res) 
                    {
                        set agree = agree + 1;
                    }

                    // Count the number of ones we saw:
                    if (res == One)
                    {
                        set numOnes = numOnes + 1;
                    }
                }

                Set(Zero, qubits[0]);
            }
            // Return number of times we saw a |0> and number of times we saw a |1>
            return (count-numOnes, numOnes, agree);
        }
    }
}

Este es un ejemplo de lanzar la moneda con entrelazamiento cuántico en Q#, el lenguaje cuántico de Microsoft.

Referencias

Quantum Computing for Computer Scientists
Cats, Qubits, and Teleportation: The Spooky World of Quantum Algorithms
Microsoft Quantum Development Kit: Introduction and step-by-step demo
Qubit

La entrada Computación cuántica para torpes: introducción para programadores se publicó primero en Adrianistán.

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

Adrianistán

Usar AVA para tests en una API hecha en Node.js y Express

julio 15, 2018 07:01

Testear nuestras aplicaciones es algo fundamental si queremos garantizar un mínimo de calidad. En este breve post, explicaré como he usado AVA para crear tests para mis APIs: tanto unitarios como de integración con AVA.

AVA es una herramienta de testing, que nos permite describir los tests de forma muy sencilla. De todas las herramientas que he probado, AVA es muy preferida. Es muy sencilla, ejecuta los tests en paralelo y permite escribir código en ES6 (usa Babel por debajo). Además tiene bastante soporte siendo el framework de testing usando por muchos proyectos ya.

Instalamos AVA de la forma típica:

npm install ava --save-dev

A continuación creamos un fichero cuya terminación sea .test.js, por ejemplo, suma.test.js. El lugar da igual.

Una opción de diseño es poner los test unitarios al lado de las funciones en sí, otra opción es crear una carpeta para todos los tests, ya que los de integración van a ir casi siempre ahí. Para ejecutar los tests, simplemente:

ava

El interior de suma.test.js debe importar la función test de AVA y las funciones que quiera probar.

Los tests se definen como una llamada a la función test con la descripción del test y a continuación un callback (asíncrono si queremos) con el objeto que controla los tests (llamado t normalmente). Veamos un ejemplo simple:

import test from "ava";
import {suma} from "./operaciones";

test("Suma",t => {
    t.is(suma(1,2),3);
});

El objeto t soporta múltiples operaciones, siendo is la más básica. Is pide que sus dos argumentos sean iguales entre sí, como Assert.Equal de xUnit.

Veamos que más soporta Ava.

  • t.pass(): Continúa el test (sigue)
  • t.fail(): Falla el test (no sigue)
  • t.truthy(val): Continúa el test si val es verdaderoso (usando la lógica de JavaScript) o falla el test
  • t.true(val): Continúa el test si val es verdadero (más estricto que el anterior) o falla.
  • t.is(val1,val2): Continúa el test si val1 y val2 son iguales (superficialmente) o falla.
  • t.deepEqual(val1,val2): Continúa el test si val1 y val2 son iguales (profundamente) o falla.
  • t.throws(funcion): Ejecuta la función especificada esperando que lance una excepción. Si no lo hace, falla el test. Se puede especificar el tipo de excepción que esperamos en el segundo argumento.
  • t.notThrows(funcion): Exactamente lo contrario que la anterior.

Y algunas más, pero estas son las esenciales.

import test from "ava";

function sum(a,b){
    return a+b;
}

function login(username,password){
    if(username === null || password === null){
        throw new Error("Missing username or password");
    }
}

test("Test example: Sum",t => {
    t.is(sum(1,2),3);
});

test("Login fail username null", t => {
    t.throws(()=>{
        login(null,"123456");
    });
});
test("Login fail password null", t => {
    t.throws(()=>{
        login("username",null);
    });
});

También podemos definir funciones que se ejecuten antes y después de nuestros tests, y una sola vez o con cada test. Podemos usar test.before, test.beforeEach, test.after y test.afterEach en vez de test. Por ejemplo, si tienes una base de datos que necesita inicialización, puedes definir ese código en test.before y la limpieza en test.after.

import test from "ava";
import db from "../db";

test.before(async () => {
    // Iniciar el ORM Sequelize
    await db.sync();
});

 

Con esto ya podemos hacer tests unitarios, pero no podemos probar la aplicación web al 100%. Entra en acción supertest que nos permitirá tener un servidor Express simulado para que los tests puedan probar la aplicación al completo.

Supertest

Instalamos supertest

npm install supertest --save-dev

En el fichero de test necesitamos crear un objeto de tipo aplicación de Express. Este objeto puede ser el mismo que usas en tu aplicación real o ser una versión simplificada con lo que quieras probar.

import test from "ava";
import request from "supertest";
import auth from "http-auth";
import express from "express";

function setup(){
    const app = express();

    let basic = auth.basic({
        realm: "Upload Password"
    }, function (username, password, callback) {
        callback(username === "admin" && password === "123456");
    });

    app.get("/upload",auth.connect(basic),function(req,res){
        res.sendFile("upload.html");       
    });
    return app;
}

test("Página upload requiere autenticación HTTP Basic", async t => {
    let res = await request(setup())
        .get("/upload")
        .send();
    t.is(res.status,401);
    t.true(res.header["www-authenticate"] !== undefined);
});

Aquí la función de test es asíncrona, AVA es compatible con ambos tipos. Supertest es muy completo y permite probar APIs enteras con su sencilla interfaz que junto con AVA, se convierte en algo casi obligatorio para una aplicación que vaya a producción.

La entrada Usar AVA para tests en una API hecha en Node.js y Express se publicó primero en Adrianistán.

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

Blog Bitix

La librería log4j2 para emitir trazas en aplicaciones Java

julio 14, 2018 11:00

Java

La librería log4j2 es la librería sucesora de log4j y logback para emitir las trazas de depuración e información indispensables cuando son requeridas en una aplicación Java. Suele usarse en combinación slf4j ya que esta permite cambiar de librería de logging subyacente sin hacer ningún cambio en la aplicación.

Algunas de las nuevas características de log4j2 son:

  • Rendimiento mejorado usando funcionalidades asíncronas en los loggers.
  • Soporte para múltiples APIs como SL4J, Commongs Logging y java.util.logging (JUL).
  • Sin encadenamientos a la librería al poder en cualquier momento usar cualquier librería compatible con SLF4J.
  • Recarga automática de la configuración sin perder ninguna traza.
  • Filtrado avanzado basado en datos de contexto, marcadores, expresiones regulares y otros componentes.
  • Arquitectura basada en plugins.
  • Soporte de propiedades definidas en archivos de configuración, propiedades del sistema, variables de entorno, el mapa ThreadContext y datos del evento.
  • Soporte de lambdas de Java 8. Las expresiones lambdas no se evalúan si no está activado el nivel de log consiguiendo el mismo efecto que con una sentencia if pero en menos código.
  • Niveles de log personalizados fácilmente definibles sin necesidad de realizar subclases.
  • Recolección de basura reducida lo que evita presión al recolector de basura y mejora el rendimiento de las aplicaciones.

Por defecto, la configuración de log4j2 se describe en un archivo xml aunque también soporta definirlo en un formato menos verboso como yaml. La siguiente aplicación de Spring Boot al iniciarse emite en la consola varios mensajes usando log4j2.

Usando la clase LogManager se obtiene una referencia a la clase Logger con la que se emiten las trazas y que posee diferentes métodos para cada nivel de traza.

Una vez se ha iniciado la aplicación Spring Boot invoca el método run y se emiten las trazas propias de la aplicación después de las que también Spring Boot y otras librerías emiten., en este caso usando texto en forma de arte ascii.

En el archivo de construcción de la aplicación usando Gradle hay que incluir las dependencias de las librerías.

Las trazas son muy importantes por la valiosa información que proporcionan de lo que está sucediendo en una aplicación a los que recurriendo es posible obtener la valiosa información que permite saber que ha sucedido en una determinada acción o que permite descubrir mucho más rápidamente la causa de un error.

En otro artículo ya he comentado como usar marcadores con sl4j y logback para relacionar trazas que son emitidas en diferentes clases o módulos de la aplicación, también se puede hacer con log4j. En siguientes artículos comentaré como transformar un objeto a un mensaje sin necesidad de convertir ese objeto a un String en cada traza de la aplicación donde se use su información y otra forma de relacionar trazas muy útil en aplicaciones web asignando un indenticador única en cada petición y emitiendolo en todas sus trazas, esto permite saber todo lo que ha ocurrido en una petición entre las muchas que se ejecutan concurrentemente por todos los usuarios de la aplicación.

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

La librería log4j para emitir trazas en aplicaciones Java

julio 14, 2018 11:00

Java

La librería log4j es la librería sucesora de log4j y logback para emitir las trazas de depuración e información indispensables cuando son requeridas en una aplicación Java. Suele usarse en combinación slf4j ya que esta permite cambiar de librería de logging subyacente sin hacer ningún cambio en la aplicación.

Algunas de las nuevas características de log4j2 son:

  • Rendimiento mejorado usando funcionalidades asíncronas en los loggers.
  • Soporte para múltiples APIs como SL4J, Commongs Logging y java.util.logging (JUL).
  • Sin encadenamientos a la librería al poder en cualquier momento usar cualquier librería compatible con SLF4J.
  • Recarga automática de la configuración sin perder ninguna traza.
  • Filtrado avanzado basado en datos de contexto, marcadores, expresiones regulares y otros componentes.
  • Arquitectura basada en plugins.
  • Soporte de propiedades definidas en archivos de configuración, propiedades del sistema, variables de entorno, el mapa ThreadContext y datos del evento.
  • Soporte de lambdas de Java 8. Las expresiones lambdas no se evalúan si no está activado el nivel de log consiguiendo el mismo efecto que con una sentencia if pero en menos código.
  • Niveles de log personalizados fácilmente definibles sin necesidad de realizar subclases.
  • Recolección de basura reducida lo que evita presión al recolector de basura y mejora el rendimiento de las aplicaciones.

Por defecto, la configuración de log4j se describe en un archivo xml aunque también soporta definirlo en un formato menos verboso como yaml. La siguiente aplicación de Spring Boot al iniciarse emite en la consola varios mensajes usando log4j2.

Usando la clase LogManager se obtiene una referencia a la clase Logger con la que se emiten las trazas y que posee diferentes métodos para cada nivel de traza.

Una vez se ha iniciado la aplicación Spring Boot invoca el método run y se emiten las trazas propias de la aplicación después de las que también Spring Boot y otras librerías emiten., en este caso usando texto en forma de arte ascii.

En el archivo de construcción de la aplicación usando Gradle hay que incluir las dependencias de las librerías.

Las trazas son muy importantes por la valiosa información que proporcionan de lo que está sucediendo en una aplicación a los que recurriendo es posible obtener la valiosa información que permite saber que ha sucedido en una determinada acción o que permite descubrir mucho más rápidamente la causa de un error.

En otro artículo ya he comentado como usar marcadores con sl4j y logback para relacionar trazas que son emitidas en diferentes clases o módulos de la aplicación, también se puede hacer con log4j. En siguientes artículos comentaré como transformar un objeto a un mensaje sin necesidad de convertir ese objeto a un String en cada traza de la aplicación donde se use su información y otra forma de relacionar trazas muy útil en aplicaciones web asignando un indenticador única en cada petición y emitiendolo en todas sus trazas, esto permite saber todo lo que ha ocurrido en una petición entre las muchas que se ejecutan concurrentemente por todos los usuarios de la aplicación.

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...

Bitácora de Javier Gutiérrez Chamorro (Guti)

WinAPI contra C

julio 14, 2018 09:09

Muchas veces tiendo a usar funciones implementadas en la API de Windows en vez de las nativas de C/C++. El motivo es que al estar disponibles en el propio sistema evitamos que se enlacen en nuestra aplicación, y así el ejecutable resultante sea más compacto. El inconveniente es que probablemente sean más lentas, particularmente porque […]

La entrada WinAPI contra C aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

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

Variable not found

El filtro [ApiController] en ASP.NET Core MVC 2.1

julio 13, 2018 07:13

ASP.NET Core MVCUno de los objetivos de ASP.NET Core ha sido siempre servir como infraestructura para la creación de servicios HTTP o APIs web, por lo que ya desde las primeras versiones se incluían funcionalidades específicamente diseñadas para facilitar esta tarea.

ASP.NET Core 2.1 continúa profundizando en esa línea e incluye entre sus novedades el nuevo atributo [ApiController], un decorador aplicable a controladores que los identifica como puntos de entrada de APIS, aplicando de forma automática una serie de convenciones bastante útiles a la hora de crear este tipo de componentes:
[ApiController]
[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
...
}
Fijaos que, a diferencia de ASP.NET Web API (.NET Framework), se ha optado por utilizar un atributo en lugar de emplear herencia (en aquél framework existía la clase ApiController).
A continuación veremos qué debemos tener en cuenta a la hora de aplicar este atributo a nuestros controladores y qué convenciones son las que estaremos asumiendo al utilizarlo.

Obligatoriedad de usar rutas explícitas

El atributo [Route] es obligatorioLa aplicación de [ApiController] a un controlador hace obligatorio el uso de rutado por atributos hacia sus acciones. Es decir, los controladores decorados con [ApiController] no serán accesibles utilizando rutas por convenciones, como las definidas en el método Configure() de la clase Startup. De no ser así, se lanzará una excepción durante el arranque de la aplicación y no se podrá ejecutar.

Por tanto, este atributo vendrá siempre acompañado del correspondiente [Route], ya sea a nivel de controlador, o en sus acciones.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
...
}

Inferencia del origen de parámetros

Al decorar un controlador con [ApiController], el binder usará el siguiente criterio, en orden de aplicación, para determinar desde dónde obtener los valores para los parámetros de las acciones:
  • Los parámetros de tipo complejo (clases, básicamente) se obtendrán desde el cuerpo de la petición. Sería equivalente a decorar el parámetro con [FromBody] en versiones anteriores a ASP.NET Core 2.1, por lo que, como ocurría anteriormente, sólo puede existir un parámetro de este tipo en las acciones.
     
  • Los valores para los parámetros de tipo IFormFile e IFormFileCollection serán tomados desde los valores recibidos como campos de formulario. Equivalente a usar [FromForm] en versiones anteriores.
     
  • Los parámetros cuyo nombre coincida con un valor presente en la ruta de la petición serán tomados desde ella, es decir, como si estuvieran decorados con [FromRoute].
     
  • El resto de parámetros se asumirá que proceden de la query string, equivalente a aplicar FromQuery] sobre ellos.
Por ejemplo, en una acción código como la siguiente, el valor del parámetro id se obtendrá desde la ruta de la petición, Invoice desde el cuerpo de la petición, y returnUrl desde la query string, todo ello sin necesidad de especificarlo expresamente:
[ApiController]
[Route("api/[controller]")]
public class InvoiceController: ControllerBase
{
[HttpPost("update/{id}")]
public ActionResult<InvoiceCreationResult> Update(int id, InvoiceInfo invoice, string returnUrl)
{
...
}
}

Errores HTTP 400 automáticos cuando el estado del modelo es inválido

Cuando entra una petición dirigida hacia una acción perteneciente a un controlador decorado con [ApiController], el binder cargará sus parámetros y aplicará automáticamente las validaciones asociadas a los datos de entrada. Como es habitual, el resultado de dicha validación lo tendremos en la propiedad ModelState.IsValid.

Pues bien, por defecto, el framework comprobará todos los objetos recibidos, y retornará un error HTTP 400 cuando se produzcan errores de validación. A continuación puedes ver un ejemplo de acción que devuelve el mismo objeto que recibe como parámetro, y el resultado obtenido de algunas peticiones:
public class Friend
{
[Required]
public string Name { get; set; }
[Range(0, 120)]
public int Age { get; set; }
}

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpPost]
public ActionResult<Friend> Friend(Friend f)
{
return f;
}
}
PeticiónRespuesta
Enviamos un objeto correcto:
POST https://localhost:44399/api/test/ HTTP/1.1
Host: localhost:44399
Content-Length: 34
Content-Type: application/json

{
   name: "John",
   age: 34
}
HTTP 200, con el mismo objeto recibido:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 24

{"name":"John","age":34}
Objeto con contenidos inválidos en los campos:
POST https://localhost:44399/api/test/ HTTP/1.1
Host: localhost:44399
Content-Length: 31
Content-Type: application/json

{
   name: "",
   age: 200
}
HTTP 400 con el resultado de las validaciones incorrectas:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 91

{
   "Age":["The field Age must be between 0 and 120."],
   "Name":["The Name field is required."]
}
Ni siquiera enviamos un objeto:
POST https://localhost:44399/api/test/ HTTP/1.1
Host: localhost:44399
Content-Length: 0
HTTP 400 indicando un error en el objeto:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 33

{"":["The input was not valid."]}

Personalización o desactivación de algunos comportamientos

Aunque las convenciones asociadas al uso de [ApiController] son bastante razonables, si por cualquier motivo queremos desactivar alguno de los comportamientos por defecto, podemos hacerlo desde el método ConfigureServices() de la clase Startup.

En el siguiente código muestra cómo desactivarlas todas (ya, mucho sentido no tendría, pero me vale como ejemplo ;))
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = false;
options.SuppressInferBindingSourcesForParameters = false;
options.SuppressModelStateInvalidFilter = false;
});
Ojo, que estas líneas deben incluirse después de haber añadido los servicios de MVC con services.AddMvc(), porque de lo contrario no funcionará.
También podemos configurar el tipo de resultado que se retornará cuando se detecte que el estado del modelo es inválido. Por ejemplo, en el siguiente código se establece como respuesta un resultado del tipo NotFoundResult(), lo que provocará que nuestros clientes obtengan un HTTP 404 como respuesta a una petición con datos inválidos:
services.Configure<ApiBehaviorOptions>(options =>
{
...
options.InvalidModelStateResponseFactory = context => new NotFoundResult();
});
¡Y eso es todo! Espero que lo que hemos visto os sea de utilidad, y gracias a estas pequeñas novedades os resulte más sencillo el desarrollo de APIs HTTP con ASP.NET Core :)

Publicado en Variable not found.

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

Variable not found

Task.Factory.StartNew(EnjoySummer, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);

julio 12, 2018 06:55

Pues eso, que parecía que no llegaba y las semanas se hacían cada vez más cuesta arriba, pero por fin es el momento de descansar un poco e intentar conseguir un merecido reinicio mental.

Durante las próximas semanas dejaré el blog a merced de las olas, pues estaré disfrutando con la familia en las maravillosas playas de Cádiz: sol, paseos, refrescantes bañitos en las aguas del Atlántico y chiringuitos playeros con aroma a pescado fresco. Un buen plan, sin duda ;)

Costa Ballena, Cádiz
Imagen original

Nos vemos a la vuelta, ya en el mes de septiembre, con la batería al 100% y listos para seguir buscando la variable ;)

¡Felices vacaciones a todos!

Publicado con ánimo vacacional en Variable not found.

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

Adrianistán

La perlificación de Python

julio 10, 2018 09:59

You know, FOX turned into a hardcore sex channel so gradually I didn’t even notice

Marge Simpson, Los Simpson

Recientemente ha salido Python 3.7, con interesantes novedades. También han salido los primeros artículos hablando de las novedades que podrá traer Python 3.8. Como muchos ya conoceréis, y si no es así explico, Python funciona a base de PEPs (Python Enhancement Proposal). Cualquier persona puede abrir un PEP, que es un documento que describe la funcionalidad que se quiere añadir/modificar/eliminar. Estas PEPs se discuten y finalmente Guido, creador de Python, las aprueba y se codifican.

Dentro de las PEP relacionadas con Python 3.8 hay algunas bastante controvertidas que han hecho saltar la voz de alarma. No ha faltado gente que ha opinado que cada vez Python se parece más a Perl. Este proceso habría empezado con Python 3 pero se habría ido haciendo más evidente hasta llegar a hoy. Cada vez con más sintaxis poco utilizada, con más elementos, más cómodo de escribir para el experimentado aunque menos legible si no dominas el lenguaje.

Y resulta curioso, porque Python es en parte una respuesta a la excesiva complejidad que podían tener los programas hechos en Perl. Su popularidad se debe a que es fácil de aprender y eso parece que ya no está tan claro.

Con la introducción de operadores como := o ?? o anotaciones como @dataclass se va, en cierta medida, perdiendo el espíritu original de Python. Y es cierto que otros lenguajes tienen algo similar, pero precisamente Python había sido muy selecto en incluir un conjunto bastante reducido de características, que todo el mundo pudiese dominar. Al final se sacrifica legibilidad y facilidad de aprendizaje por una ergonomía que beneficia a unos pocos en unos casos concretos.

Lenguajes de programación barrocos

Universidad de Valladolid, ejemplo de barroco civil. Foto: https://artevalladolid.blogspot.com

Python lleva un tiempo entrando en un proceso de perlificación pero en mi opinión no es el único lenguaje que ha entrado en una espiral parecida. Cada vez más lenguajes han pasado del renacimiento, donde se buscaba la consistencia, la simetría, la sencillez sin perder la funcionalidad, hacia el barroco, donde los lenguajes son más recargados, con más elementos sintácticos, que cubren mejor casos concretos, pero que de por sí no son tan esenciales, no cambian aspectos fundamentales del lenguaje y normalmente introducen diversas formas de hacer algo en un mismo lenguaje.

Veamos más ejemplos: en C++20 se propuso añadir funcionalidad de dibujado 2D a la librería estándar (propuesta que fue rechazada en una historia bastante larga para este post) y se han propuesto conceptos, módulos, comparación de tres vías, reflexión, metaclases,… En C# 8.0 se han propuesto también bastantes cosas como records, tipos non-nullable, interfaces con métodos ya implementados (traits) y rangos. Y eso sin contar con las características que estos dos lenguajes ya tienen, que son bastante más extensos que Python.

Retablo lateral de la Iglesia de San Miguel y San Julián (Valladolid). Barroco puro. Foto: https://commons.wikimedia.org/wiki/File:San_Miguel_-_retablo_relicario.jpg

Hay un horror vacui, horror al vacío, a funcionalidades. Siempre se añade y casi nunca se elimina. ¿Pero es realmente necesario? Es evidente que durante mucho tiempo, los lenguajes evolucionaban de forma muy lenta y muchos de los cambios que han hecho eran necesarios. Desde hace unos años, se ha aumentado la velocidad de los cambios, pero esto no puede seguir así eternamente, porque el ritmo que llevan muchas veces es superior al de los avances en lenguajes de programación y la retrocompatibilidad impide ir quitando cosas al mismo ritmo que se añaden. De este modo, todos los lenguajes que entran en esta espiral crecen y crecen. No se llegan a pulir, simplemente crecen.

La perfección no se alcanza cuando no hay nada más que añadir, sino cuando no hay nada más que quitar

Antoine de SaintExupéry

Uno podría comparar esto con lenguajes naturales, como el español o el inglés. En estos idiomas, pese a existir reglas, existen numerosas excepciones. Es posible que lenguajes como Python se estén viendo influenciados por las mismas mecánicas humanas que rigen los lenguajes naturales y que han hecho que existan excepciones. Tendría bastante sentido que ocurriese así. Pero personalmente, me gustaría que aprender Python no costase tanto como aprender alemán o francés.

Los procesos comunitarios

Para mí, gran parte de esta sobrecarga viene dada por los procesos comunitarios. En un proceso comunitario como PEP, comité de C++ o similares es mucho más fácil añadir que eliminar. En C++ la situación ha llegado a un punto en el que Bjarne Stroustrup, creador de C++, ha pedido que se relajen con las propuestas en Remember the Vasa!, en honor a un bonito barco sueco que se hundió por exceso de carga. No tanto por su peso, sino por su disposición y las reformas que hubo que hacer para que todo encajase.

El Vasa fue recuperado después de su naufragio y se expone en Estocolmo. Foto: https://rachelannsblog.wordpress.com/2016/08/03/set-sail-set-at-the-bottom-of-the-sea/

Es por ello que las personas encargadas de elegir que se añade al lenguaje o no deben de ser muy conscientes de lo que supone, ya que una vez se introduzca, va a ser casi imposible eliminarlo.

No estoy en contra de añadir nuevas características (¡al contrario!) pero se debe respetar la visión de conjunto del lenguaje, que todo cuadre y esté cohesionado. No siempre tener más es mejor.

¿Te ha gustado el artículo?

Si es así, puedes ayudar al blog. Dentro de unos días es el Amazon Prime Day. Como muchos de vosotros seguro que os compraréis algo, no quiero dejar la oportunidad de deciros que este blog tiene enlace de referidos y que por cada cosa que compréis con el enlace, me llevo una pequeña parte (a vosotros no os va a costar más caro).

Enlace Amazon.es

Será muy bien recibido

 

La entrada La perlificación de Python se publicó primero en Adrianistán.

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

Variable not found

Problem Details: una forma estándar de retornar errores desde APIs HTTP (y cómo usarlo desde ASP.NET Core)

julio 10, 2018 06:55

ASP.NET Core MVCLos que lleváis tiempo programando APIs HTTP, ya sea con ASP.NET Core o con otras tecnologías, seguro que en muchas ocasiones habéis tenido que decidir cómo retornar al cliente, de forma más o menos normalizada, los errores producidos en el lado servidor.

Lo habitual es echar mano de los status code de HTTP para indicar problemas en el proceso de una petición; de hecho, este protocolo dispone de un rico conjunto de códigos que en principio parecen cubrir todas nuestras necesidades.

Pero no siempre es así. Por ejemplo, si tenemos un servicio que permite a los clientes de una empresa formalizar un pedido a través de un API y una llamada a este servicio retorna un error HTTP 403 (forbidden), claramente estamos indicando que el solicitante no tiene permisos para hacer un pedido. Sin embargo, no tenemos una forma clara de indicar cuál es la causa de esta prohibición (¿quizás las credenciales no son correctas? ¿o quizás el cliente no tiene crédito en la empresa? ¿o puede ser que el administrador lo haya denegado expresamente?)

Para aportar más detalles sobre el problema, normalmente necesitaremos retornar en el cuerpo de la respuesta información extra usando estructuras o formatos personalizados, probablemente distintos de una aplicación a otra, y documentarlos apropiadamente para que los clientes puedan entenderlos. Y aquí es donde entra en juego el estándar “Problem details”.

El estándar Problem details (RFC7807)

Consciente de la necesidad de normalizar este tipo de escenarios, la Internet Engineering Task Force (IEFT) propuso hace unos años el estándar Problem Details for HTTP APIs (RFC7807), promoviendo el uso de una estructura JSON normalizada en la que es posible ampliar información sobre un error producido durante el proceso de la petición a la API.

Estas respuestas, empaquetadas con un content type “application/problem+json” (aunque también podrían ser “application/problem+xml”), tienen una pinta como la siguiente:
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en

{
"status": 403,
"type": "https://www.b2bcommerce.com/orders/problems/disabled-customer",
"title": "You can't submit orders",
"detail": "You must finish your registration process before submitting orders",
"instance": "/orders/12/123456",
}
En la estructura anterior se observan varias propiedades que forman parte del estándar:
  • status, si existe, debe contener el código HTTP original del error. Puede ser interesante cuando intermediarios como proxies u otros middlewares pueden alterar el código original.
     
  • type es una URI absoluta o relativa, por defecto “about:blank”, que permite identificar cuál es el problema exacto y su posible solución. Según la especificación, el contenido de esa URI debería ser una descripción del problema legible por personas (por ejemplo, escrita usando HTML).
     
  • title puede contener un texto breve que describa el problema, sobre todo destinados a aquellos consumidores que no sean capaces de interpretar correctamente el significado del campo type.
     
  • En detail podemos especificar información adicional sobre el problema, pero más enfocada a su resolución. Como en el caso anterior, debe ser legible por humanos, y no estructuras de datos que deban ser procesadas por el consumidor.
     
  • Por último, instance es una URL, absoluta o relativa, que puede apuntar a la instancia protagonista del problema.
Aparte de estos campos, el estándar permite el uso de extensiones, es decir, de campos personalizados que aporten aún más información al cliente. El siguiente bloque muestra, por ejemplo, el resultado de una petición con valores incorrectos en sus parámetros:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Language: en

{
"type": "https://mysite.com/validation",
"title": "Invalid request parameters",
"invalid-params":
[
{
"name": "displayName",
"reason": "This field is required"
},
{
"name": "age",
"reason": "Must be a integer between 0 and 120"
},
{
"name": "email",
"reason": "Must be a valid email address"
}
]
}
Es muy recomendable leer la especificación completa en https://tools.ietf.org/html/rfc7807

Problem details en ASP.NET Core 2.1

ASP.NET Core 2.1 incluye un soporte aún muy básico para facilitar la adopción de este estándar en nuestros servicios HTTP, pero al menos nos facilita la tarea en algunos escenarios.

En primer lugar, en el espacio de nombres Microsoft.AspNetCore.Mvc encontramos la clase ProblemDetails, que modela la estructura de datos definida por la especificación:
public class ProblemDetails
{
public string Type { get; set; }
public string Title { get; set; }
public int? Status { get; set; }
public string Detail { get; set; }
public string Instance { get; set; }
}
Esto ya nos da la posibilidad de utilizarla directamente para retornar al cliente problem details, por ejemplo como sigue:
[HttpPost]
public ActionResult Submit(Order order)
{
const int maxProducts = 10;
if (order.ProductsCount > maxProducts)
{
var details = new ProblemDetails()
{
Type = "https://www.b2bcommerce.com/orders/too-many-products",
Title = "The order has too many products",
Detail = $"You can't submit orders with more than {maxProducts} products",
Instance = Url.Action("Get", "Orders", new { id = order.Id }),
Status = 403
};
return new ObjectResult(details)
{
ContentTypes = {"application/problem+json"},
StatusCode = 403,
};
}
...
}
Una secuencia de petición y respuesta para comprobar el funcionamiento del código anterior podría ser:
========================================================================
POST https://localhost:44399/api/orders HTTP/1.1
Host: localhost:44399
Content-Length: 37
Content-type: application/json

{
id: 19,
productsCount: 22
}

========================================================================
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json; charset=utf-8
Server: Kestrel
X-Powered-By: ASP.NET
Content-Length: 208

{
"type":"https://www.b2bcommerce.com/orders/too-many-products",
"title":"The order has too many products",
"status":403,
"detail":"You can't submit orders with more than 10 products",
"instance":"/api/Orders/19"
}
Aunque crear el ObjectResult de forma manual es algo farragoso, sería bastante sencillo crear un helper o action result que nos ayudara a construir y retornar este tipo de objetos.
Tenemos también la clase ValidationProblemDetails, que, heredando de ProblemDetails, está diseñada expresamente para retornar errores de validación estructurados. Esta clase añade la propiedad Errors que es un diccionario en el que las claves son los nombres de las propiedades validadas con error, y el valor contiene un array de strings describiendo los problemas encontrados.

De hecho, esta clase está tan enfocada a los errores de validación que incluso podemos instanciarla enviándole un ModelState:
[HttpPost]
public ActionResult Submit([FromBody]Order order)
{
if (!ModelState.IsValid)
{
var details = new ValidationProblemDetails(ModelState);
return new ObjectResult(details)
{
ContentTypes = {"application/problem+json"},
StatusCode = 400,
};
}
return Ok(order);
}
Para simplificar aún más este escenario, también se ha añadido en ControllerBase el método ValidationProblem(), que automáticamente retornará un error 400 con la descripción de los errores:
[HttpPost]
public ActionResult Submit([FromBody]Order order)
{
if (!ModelState.IsValid)
{
return ValidationProblem();
}
...
}
Misteriosamente, el retornar el resultado de la invocación a ValidationProblem() no establecerá el content type a "application/problem+json", cuando esto tendría bastante sentido. Supongo que será para evitar que exploten clientes que no estén preparados para recibir ese tipo de contenido.
En cualquier caso, el resultado devuelto por el servicio anterior podría ser el siguiente:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Server: Kestrel
X-Powered-By: ASP.NET
Content-Length: 188

{
"errors": {
"ProductsCount": ["The field ProductsCount must be between 1 and 10."]
}
,
"type":null,
"title":"One or more validation errors occurred.",
"status":null,
"detail":null,
"instance":null
}

¿Y cómo se integra esto con el filtro [ApiController]?

Hace unos días hablábamos del nuevo filtro [ApiController], que permite identificar un controlador como endpoint de un API, y aplicarle automáticamente usa serie de convenciones, como el retorno automático de errores 400 cuando se detectan errores de validación.

También veíamos que, jugando un poco con su configuración, era posible establecer la factoría de resultados para las respuestas a peticiones con problemas de validación, por lo que resulta sencillo utilizar este punto de extensión para crear y configurar la respuesta bad request alineada con el estándar:
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new ValidationProblemDetails(context.ModelState)
{
Instance = context.HttpContext.Request.Path,
Status = 400,
Type = "https://myserver.com/problems/validation",
Detail = "Invalid input data. See additional details in 'errors' property."
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json", "application/problem+xml" }
};
};
});
¡Y eso es todo, al menos de momento! Espero que os haya resultado interesante, y útil para conocer este nuevo estándar y su, aunque aún básica, integración en ASP.NET Core.

Publicado en Variable not found.

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

Variable not found

Enlaces interesantes 329

julio 09, 2018 06:55

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

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

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

Koalite

APIs con Node, TypeScript, Koa y TypeORM

julio 09, 2018 05:06

En mi (parsimoniosa) búsqueda de plataformas alternativas a .NET más de una vez me he llegado a plantear el uso de NodeJS + TypeScript. A fin de cuentas NodeJS es multiplataforma, ya tiene cierta madurez y es supuestamente estable. Con TypeScript llevo ya un par de años y, pese a sus “cosillas”, estoy razonablemente contento con él.

Por eso cuando hace unos días contactó conmigo Javier Avilés para hablarme sobre un repositorio que ha montado como plantilla para crear APIs con Node, TypeScript, Koa y TypeORM me pareció interesante echarle un vistazo.

OJO: Si has llegado aquí buscando un tutorial sobre cómo crear APIs con Node y TypeScript, mejor léete directamente la documentación de la plantilla creada por Javier. Ahí encontrarás información más útil que en este post. Aquí me limitaré a hacer un repaso somero a lo que me ha parecido la plantilla.

Las acciones web

De NodeJS no hay mucho que contar a estas alturas, y sobre TypeScript ya he escrito suficiente, por lo que intentaré centrarme en otras cosas. Puesto que el proyecto es para montar un API Web, parece razonable empezar por ver qué tipo de servidor web y framework se está usando.

Como framework para crear el API se está usando koa. Sin estar muy metido en node, diría que es una de las librerías/frameworks más populares para servidores web junto con express (del que incluso escribí un tutorial cuando hacía ese tipo de cosas). Conocía koa un poco por sus primeras versiones y recuerdo que hacía un uso llamativo de los generadores para huir del callback hell. En las versiones actuales se aprovecha async/await para conseguir dar un aspecto líneal al código asíncrono haciéndolo bastante claro y legible.

Al igual que la mayoría de frameworks web de lenguajes que no viven encorsetados en clases, la gestión de peticiones y respuestas se realiza mediante funciones que reciben la petición y generan la respuesta, en este caso ambas empaquetas en un objeto context. Lo bueno de este enfoque es que evita código repetitivo y ayuda a que todo sea más fácil de componer:

app.use(async ctx => {
  ctx.body = 'Hello World';
});

Es un estilo que me gustó mucho cuando conocí node, y más aún cuando jugué con él en clojure. Lo cierto es que la filosofía de la web, basada en petición/respuesta, se ajusta muy bien a ser modelada con funciones en lugar de con clases. Además, eso hace que todo sea muy homogéneo, ya conceptos típicos de frameworks webs como middlewares se reducen a crear decoradores sobre funciones.

Precisamente esta es una de las cosas que no me convencen de la plantilla que estamos analizando: el uso de clases estáticas para agrupar estas funciones que sirven de manejadores de peticiones:

export default class UserController {
  public static async getUsers (ctx: BaseContext) {
    // ...
  }
}

El uso de una clase para encapsular en método estático no aporta nada (a menos que me esté perdido algo) y en lenguajes como TypeScript lo consideraría un code smell.

Las rutas

En cualquier aplicación web acaba siendo necesario gestionar rutas, y en este caso se está usando koa-router. No tengo ni idea de si hay más routers para koa o de lo bueno o malo que es éste, pero parece fácil de manejar con el típico API basado en métodos con los nombres de los verbos:

var app = new Koa();
var router = new Router();

router.get('/', (ctx, next) => {
  // ctx.router available
});

app
  .use(router.routes())
  .use(router.allowedMethods());

Entre las librerías que conozco hay varias opciones para gestionar el registro de rutas. Algunas se basan puramente en convenciones (al estilo RoR), otras permiten utilizar metainformación en forma de atributos o anotaciones (uno de los varios estilos soportados ASPNET MVC), y las hay que registran las rutas explícitamente.

En el caso de la plantilla se está haciendo un registro explícito de las rutas y además se hace todo en un único fichero:

// routes.ts

router.get('/users', controller.user.getUsers);
router.get('/users/:id', controller.user.getUser);
// ...

Personalmente me gusta ese estilo. Creo que es cómodo tener centralizada la definición de rutas y poder comprobar de un vistazo qué rutas existen en la aplicación y qué formato siguen. En el caso de que el número de rutas creciese mucho siempre se podría partir en varios ficheros por áreas de aplicación.

La contrapartida que tiene es que cuando añades un nuevo manejador de rutas necesitas tocar en dos sitios: el fichero donde defines la función y el fichero que contiene el registro de rutas. Para mi es un mal menor, pero no deja de ser un incordio y puede dar lugar a fallos si te olvida registrar la ruta del manejador que acabas de crear.

Una cosa que me gusta de la plantilla es que no se usa un contenedor de inversión de control. He visto otras implementaciones de este estilo que tratan de copiar demasiado los patrones de uso de plataformas como .NET y Java, y acaban replicándolas pieza a pieza con componentes que no son realmente necesarios.

Los datos

Para tratar con los datos se está usando TypeORM, una librería que se define a si misma como un ORM que soporta los patrones de Active Record y Data Mapper.

Para mi eso se queda un poco lejos de lo que espero de un ORM. Como Data Mapper estaría al nivel de micro ORMs tipo Dapper o Massive. Como Active Record… bueno, la verdad es que excepto las inspiradas en Rails, no conozco muchas librerías que hayan triunfado con ese modelo.

En muchos casos la mejor opción es usar un Data Mapper que te simplifique la generación de consultas y el mapeo de resultados a objetos. Pero hay que tener en cuenta las partes que te estás perdiendo por uno usar un ORM completo, y que van mucho más allá de generar automáticamente SQL y materializar objetos: identity map, unit of work, persistencia por alcance, etc.

El uso de TypeORM parece sencillo, aunque si eres de los que piensa que tu modelo debe ignorar la persistencia, vete olvidando. Todo se basa en el uso de decoradores sobre tus entidades:

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        length: 80
    })
    @Length(10, 80)
    name: string;

    @Column({
        length: 100
    })
    @Length(10, 100)
    @IsEmail()
    email: string;
}

No me apasiona la idea de contaminar mi modelo de dominio con cosas relacionadas con la base de datos, pero lo cierto es que en el momento en que decides usar TypeORM me da la sensación de que tampoco es que vayas a tener un modelo de dominio muy rico y vas a usar otro tipo de diseño, así que no es tan grave.

En la plantilla se utiliza también class-validator, una librería para incluir reglas de validación de forma declarativa en nuestras “entidades”. De ahí sale por ejemplo el decorador IsEmail del ejemplo anterior.

Hace muchos años escribí en contra de mezclar la validación con las entidades y sigo pensando lo mismo. Una entidad por definición debería mantener sus invariantes y no puede encontrarse en un estado inválido. Sin embargo, por mucho que aquí las llamemos entidades no estamos trabajando más que con DTOs, por lo que tiene más sentido unirlos a las reglas de validación.

Actualización: Me explica Javier que realmente TypeORM sí que tiene persistencia por alcance (usando los decoradores correspondientes) e incluso un cierto soporte para UoW a través de transacciones.

El proyecto

El proyecto usa las herramientas típicas de node y TypeScript. Además permite usar docker para facilitar las pruebas usando una máquina ya preparada con postgreSQL.

No sabía que se podía usar la propiedad engines en el fichero package.json para fijar las versiones de node y npm que requiere el proyecto, pero me ha parecido muy útil teniendo en cuenta lo (no) estable que es todo el ecosistema de Javascript.

Para la parte de TypeScript usa ts-node, que permite ejecutar TypeScript en node compilándolo al vuelo. Lo conocía por haberlo usado con alguna librería de testing. Aun así, para la compilación de producción se está generando javascript, imagino que para evitar tener que pagar el coste de la compilación. No sé si realmente es necesario (supongo que una vez compilado queda todo en memoria y no hay que estar recompilando nada en cada petición), pero parece más limpio y evitas tener la dependencia sobre TypeScript en el entorno de producción.

El código está organizado en carpetas basadas en roles (controllers, entities, …). Nuevamente es cuestión de gustos, pero prefiero organizarlo en base a funcionalidades porque creo que es mejor tener cerca físicamente los ficheros que vas a tener que tocar a la vez.

Echo de menos algunos tests, aunque sean de ejemplo. Cuando desarrollas un API Web muchas veces no tiene mucho sentido recurrir a tests unitarios, y montar test de integración supone más esfuerzo, por lo que tener un ejemplo de cómo se podrían escribir vendría bien. Teniendo además la imagen de docker para levantar la base de datos imagino que no debería ser demasiado complicado preparar el entorno.

En resumen

Me ha gustado la plantilla que ha preparado Javier para este tipo de proyectos. Puede servir como base para desarrollar un API Web basada en TypeScript y Koa, y es lo bastante pequeña como para que pueda leerte todo el código en un rato y entenderlo sin problemas. Entre las cosas que me han gusto un poco menos está el uso (para mi innecesario) de métodos estáticos y la organización basada en roles en lugar de funcionalidades, pero son detalles menores.

Se agradece que no haya intentado meter demasiadas cosas innecesarias y que no sea una copia directa de un proyecto en Sprint o ASP.NET MVC, como pasa en otras plantillas basadas en TypeScript que tiene un aspecto demasiado enterprise/javero/nettero para mi gusto.

Posts relacionados:

  1. TypeScript, un lenguaje diseñado para las herramientas
  2. Extender tipos existentes en TypeScript
  3. Mantenimiento de invariantes en TypeScript

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

Bitácora de Javier Gutiérrez Chamorro (Guti)

C++ Builder in the requirements

julio 08, 2018 11:43

Como integrante del programa MVP de Embarcadero fui invitado para confeccionar a un call for papers acerca de C++ Builder. El tiempo apremiaba, así que opté por desarrollar con mayor formalisto, que no rigurosidad, lo que os explicaba en ¿Por qué C++ Builder. De esa manera es como surgió mi autoría en el memorando (whitepaper) […]

La entrada C++ Builder in the requirements aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

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

Navegapolis

Scrum no se logra con pensamiento de grupo, sino con inteligencia colaborativa

julio 07, 2018 04:29

Un equipo autogestionado no tiene por qué ser ágil, ya que al igual que ocurre con los individuos, los equipos se conducen por su propia inteligencia —inteligencia colectiva—(1) que puede ser colaborativa, o simplemente pensamiento de grupo.

Aplicar prácticas ágiles en equipos autogestionados no es suficiente. Se necesita además inteligencia colaborativa.

Los equipos ágiles actúan con inteligencia colaborativa, no con pensamiento de grupo.

Inteligencia colaborativa:

Es una forma de inteligencia emergente de la acción de muchos individuos que interactúan entre sí en el contexto de la cultura digital. En ella cada individuo actúa de manera autónoma desde su posición, participando en una red para crear algo o solucionar algún problema.

Wikipedia-es "Inteligencia colaborativa" 8-jul-2018

Pensamiento de grupo:

Pensamiento de grupo (groupthink en inglés) es una expresión acuñada por el psicólogo Irving Janis en 1972 para describir el proceso por el cual un grupo puede tomar decisiones malas o irracionales. En una situación de pensamiento en grupo, cada miembro del grupo intenta conformar su opinión a la que creen que es el consenso del grupo. Esto parece ser una manera muy racional de afrontar la situación. Sin embargo, resulta en una situación en la cual el grupo en definitiva se pone de acuerdo en determinada acción que cada miembro individualmente considera desaconsejable.

La definición original de Janis era: «Un modo de pensamiento que las personas adoptan cuando están profundamente involucradas en un grupo cohesivo, cuando los esfuerzos de los miembros por unanimidad hacen caso omiso de su motivación para valorar realísticamente cursos de acción alternativos» ... El pensamiento de grupo tiende a ocurrir en comités y en grades organizaciones.

Wikipedia-es "Pensamiento de grupo" 8-jul-2018

 

 

pensamiento de grupo

(1) Referencias - MIT - Anita Williams.

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

Blog Bitix

Pruebas funcionales con Geb en una aplicación web Java

julio 06, 2018 10:30

Geb
Java

Las pruebas automatizados permiten comprobar que una aplicación se comporta de la manera esperada en los casos probados, creando pruebas automatizadas se evita hacer las aburridas y repetitivas pruebas manuales que consumen gran cantidad de tiempo. Hay diferentes categorías de pruebas: unitarias, de integración y las funcionales. Para las pruebas unitarias y de integración en la plataforma Java unas de las más populares herramientas son JUnit y Spock, para las pruebas funcionales están Selenium y la herramienta que explicaré en este artículo Geb que puede usarse junto con Spock haciendo que el código de los teses sea muy descriptivo.

Geb soporta una APi similar a la que emplea jQuery para la interacción con los elementos de la página bajo pruebas. Este ejemplo prueba a ir a la página del buscador Google y que el resultado es el esperado usando alguna de las múltiples formas de interacción con el contenido de la página.

Para hacer más sencillos las pruebas se pueden describir las páginas, sus URLs y los elementos que contienen. Para ello hay que crear una clase que extienda de Page y definir propiedades como url, at y content, se pueden incluso definir métodos. En la documentación de Geb está más detallado la definición de las páginas. También se pueden crear módulos para definir elementos comunes a varias páginas.

La automatización de las pruebas además de comprobar que la página devuelva es la esperada, en el caso de estos ejemplo con una propiedad meta o con el título de la página, consiste en realizar las interacciones que un usuario realizaría como introducir datos en formularios, hacer clic en elementos de una página y comprobar la presencia de elementos que valide la prueba. Con esta herramienta se pueden probar los casos y flujos funcionales más importantes de la aplicación como sería el proceso de compra en una aplicación de comercio electrónico y la búsqueda, alta y modificación de un registro en la aplicación de gestión.

En este ejemplo se realiza una búsqueda en Google y se comprueba el resultado devuelto. Primero se accede a ella con la sentencia to, se introduce el texto de búsqueda Chuck Norris y se pulsa el botón de búsqueda. La comprobación consiste en asegurar que la página devuelta es la página de resultados de búsqueda con la sentencia at y que el primer resultado de la búsqueda contiene la palabra Chuck, GoogleHomePage define la página de búsqueda, GoogleResultsPage la página de resultados y GoogleSearchSpec contiene la interacción de la prueba.

Las dependencias de Geb necesarias a incluir en el archivo de configuración de la herramienta de gestión del proyecto usando Gradle son las siguientes:

Usando una aplicación con Spring Boot para ejecutar los teses de integración se debe iniciar la aplicación web previamente con la anotación SpringBootTest mostrado en el caso IndexSpec además de excluir los teses de integración y los unitarios de los funcionales. En el ejemplo con la tarea de Gradle integrationTest se ejecutan los teses de funcionales de Geb.

Con Gradle el informe de resultados de las pruebas se genera en build/reports/tests/integrationTest en una colección de páginas HTML con el siguiente aspecto. Si hubiese algún error se mostraría un mensaje descriptivo del fallo ocurrido. La prueba IndexSpec tarda medio minuto ya que previamente ha de iniciar el servidor de aplicaciones con la aplicación, en este caso usando Spring Boot.

Informe de las pruebas funcionales

Algunas otras herramientas que son ampliamente usadas en proyectos Java son las que comentaba en el artículo Nueva visita a herramientas para un proyecto Java.

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 integrationTest.

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

Poesía Binaria

Píldora: Utilizar arrays con nombre dinámico en Bash

julio 04, 2018 07:34

Arrays con nombre dinámico en Bash

Una de las grandes ventajas de los lenguajes interpretados es que podemos almacenar en una variable el nombre de otra variable y trabajar con su valor. ¿Punteros? Bueno, al final es parecido a la utilización de punteros en C, aunque aquí podemos hacer referencia por el mismo nombre de la variable incluso, previo filtrado y con mucho cuidado, preguntarle al usuario el de la variable para trabajar con ella.

Esto nos puede dar mucha flexibilidad en nuestro código. Por ejemplo, yo lo he utilizado para perfeccionar mi script de lectura de ficheros de configuración al que le podemos pasar también el nombre de la variable donde almacenar la configuración.

Arrays con nombre dinámico

En Bash, acceder a una variable cuyo nombre está contenido dentro de otra variable es sencillo, podemos hacerlo llamando a ${!NOMBREDEVARIABLE}. Podemos ver un ejemplo en el post referenciado.
El problema es que cuando son arrays no es tan inmediato. El objetivo es que, teniendo una variable que contiene el nombre del array podamos acceder a su información de una forma sencilla. Y El problema aquí viene en la versión de Bash que estemos utilizando. En este caso debemos hacerlo de una forma u otra dependiendo de si utilizamos una versión inferior a la 4.3 (más o menos salió en 2014) o si tenemos una versión posterior.

Ejemplo para Bash < 4.3

Lo siento, mira que no me gusta utilizar eval, pero claro, para estas versiones del intérprete necesitamos crear una cadena de caracteres con la llamada que vamos a hacer y luego evaluar dicha cadena de caracteres. Esto nos dará el valor que buscamos. Por ejemplo:

1
2
3
4
5
6
MIARRAY=("cero" "uno" "dos" "tres")
REFERENCIA="MIARRAY"

# Lo que necesitamos es acceder a cada uno de los elementos de MIARRAY a través de REFERENCIA.

eval echo \${$REFERENCIA[2]}

Si os fijáis, al construir la cadena. El primer símbolo de dólar ($) lo escapamos con la barra para que no se interprete. El que sí tiene que interpretar es el segundo, para que $REFERENCIA se transforme en el nombre del array.

Lo podemos hacer con arrays asociativos también sin problema, pero es algo complicado y realizamos demasiadas tareas para hacer referencia a una variable. Además no queda muy elegante o intuitivo tener que escapar un dólar y otro no.

Ejemplo para Bash >= 4.3

Para estas versiones, disponemos de un modificador en la orden declare (también llamada typeset) a través del cual podemos crear una variable de referencia que se comportará exactamente igual que nuestro array:

1
2
3
4
MIARRAY=("cero" "uno" "dos" "tres")
declare -n REFERENCIA="MIARRAY"

echo ${REFERENCIA[2]}

Además, de esta forma, el nombre de la variable puede venir dado en otra variable, con lo que podemos permitir que el usuario especifique el nombre de variable. Eso sí, con mucho cuidado.

Foto principal: unsplash-logoRuben Ortega

The post Píldora: Utilizar arrays con nombre dinámico en Bash appeared first on Poesía Binaria.

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

Blog Bitix

Qué es un SUT, fake, Stub, Mock y Spy en las pruebas con un ejemplo

julio 04, 2018 08:00

Las pruebas unitarias y de integración tratan de probar que el comportamiento del sujeto bajo prueba es el esperado. Este sujeto bajo prueba usará colaboradores que en las pruebas deben ser reemplazados por fakes para probar las condiciones deseadas del de sujeto bajo prueba. Depdendiendo del comportamiento asignado al colaborador tenemos varios tipos: Stub, Mock o Spy.

Al desarrollar pruebas unitarias o de integración se emplean varios términos que no son imprescindibles conocer para hacer una prueba pero si es recomendable conocerlos para saber que diferencias hay entre ellos y saber que posibilidades tenemos para construir la prueba. Los términos básicos son sujeto bajo prueba o subject under test, fake, mock, stub y spy.

En una prueba el objeto bajo prueba es el elemento del que se quiere probar su comportamiento y que las pruebas verificarán que para los casos de prueba funciona según lo esperado. Dado que raramente el sujeto bajo prueba funciona de forma aislada sino que que se comunica con otros elementos estos colaboradores han de reemplazarse preprogramado su comportamiento, a los colaboradores se les denomina impostores o fakes ya que no son los objetos reales que se usan en el programa en ejecución pero simulan su comportamiento.

Según el tipo de fake que sea un objeto están los siguientes tipos:

  • Stub: es un fake al que se le reprograman sus valores de retorno y se proporciona al objeto bajo prueba teniendo control sobre cuales son los valores que usa el sujeto bajo control.
  • Mock: es un fake que después de terminada la prueba puede ser examinado para comprobar si las interacciones con el sujeto bajo prueba han sido las correctas, se puede probar si un método ha sido llamado o cuántas veces ha sido llamado junto con sus parámetros. Adicionalmente al igual que los Stub puede preprogramarse su comportamiento. Si solo se necesita preprogramar los resultados se suele utilizar un Stub y solo cuando además se necesitan verificar las interacciones se usa un Mock.
  • Spy: es un fake en el que solo un subconjunto de métodos son fake y a menos que explícitamente sean mockeados el resto de métodos son los reales.

Los colaboradores permiten hacer los casos de prueba deterministas haciendo que siempre produzcan el mismo resultado y las pruebas no se vean afectados por condiciones externas, los colaboradores evitan efectos colaterales, evitan depender del entorno de computación, hacen que las pruebas sean rápidas por no necesitar de sistemas externos como una base de datos o servicio de red y permiten probar comportamientos inusuales en un entorno real.

En el ejemplo se quiere probar un sistema de alarma que cuando detecte en tres mediciones consecutivas que la temperatura está por encima de cierto valor suene una alarma. Los elementos de este sistema serán una clase Monitor que obtiene las temperaturas de un sensor y si detecta la condición de una temperatura elevada hace sonar una alarma. El monitor será el sujeto bajo prueba y el sensor y la alarma los colaboradores. El sensor será un fake de tipo Stub ya que solo se necesita preprogramar sus valores de retorno de temperaturas y la alarma un fake de tipo Mock ya que se debe comprobar que el monitor ha llamado una vez al método que haría sonar la alarma, esto es se necesitan verificar las interacciones.

Sistema de control de temperatura simulado

Se pueden hacer más casos de prueba como por ejemplo probar que tras dos temperaturas altas siendo la siguiente baja y posteriormente otra alta la alarma no es disparada.

En el libro Java Testing with Spock explican toda esta teoría básica de pruebas además de explicar bastante detalladamente todas las posibilidades de la herramienta de testing Spock.

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 test.

Referencia:

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

Variable not found

Enlaces interesantes 328

julio 02, 2018 02:00

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

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

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

Variable not found

¡Microsoft MVP 2018-2019!

julio 02, 2018 06:58

MVP Award ProgramEn esta ocasión sólo quería compartir con vosotros la gran alegría que supone ser honrado de nuevo con el galardón Microsoft MVP por las contribuciones y aportaciones realizadas a la comunidad de desarrolladores a lo largo del año pasado.

Son ya ocho años consecutivos y debería haberme acostumbrado a este subidón de primeros de julio, pero, como no suelo ser consciente de que ha llegado el día de los nombramientos, sigo recibiendo el conocido email de nombramiento casi con la misma emoción que aquella primera vez en que me pilló completamente desprevenido.

Muchas gracias a todos vosotros, amigos y amigas de Variable not found, porque si estoy aquí es porque me habéis regalado vuestro incondicional apoyo durante todos estos años.

Muchas gracias también al equipo del programa MVP en Microsoft y, en particular, a nuestra querida MVP lead Cristina González, por el gran trabajo que realizan.

Muchas gracias todos los que dedicáis vuestro tiempo la comunidad, seáis MVP o no, porque hacéis posible que todos aprendamos y mejoremos un poco más cada día.

Y, por supuesto, muchas gracias también a mi mujer e hijas, Inma, Inmita y María, porque ellas son las que permiten, con su infinita paciencia y comprensión, que pueda dedicar a mi pasión por el software más de lo que estrictamente requieren mis obligaciones profesionales.

Aprovecho también para enviar mi enhorabuena a los MVP renovados recientemente, y un caluroso abrazo de bienvenida a los que lo han sido por primera vez (es un momento muy bonito, ¡disfrutadlo!)

Sólo espero estar a la altura de esta distinción, compartida con gente de todo el mundo a la que admiro tanto personal como profesionalmente, y entre los que me enorgullezco de contar con grandes amigos. Y ahora, con esta dosis extra de motivación, ¡a seguir trabajando!

Desarrollador supermotivado
Source: Giphy

Publicado en Variable not found.

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

Blog Bitix

Decidido a comprar un Intel NUC entre las opciones que he evaluado

julio 01, 2018 05:30

Intel

El ordenador portátil que tenía sigue siendo perfectamente útil, sin embargo, en algunos casos concretos de mi uso la cantidad de memoria que tenía se me estaba quedando corta y cuando me pasa esto suelo aprovechar si alguien de mi entorno necesita ordenador traspasarlo y yo comprarme otro nuevo.

El ordenador que tenía era un Sony VAIO de finales del 2012 con un procesador Intel i3 3210M con gráfica integrada HD 4000, 8 GiB de memoria, almacenamiento de estado sólido Samsung 840 EVO de 256 GiB con interfaz SATA y una pantalla de resolución 1600x900. Durante todo este tiempo me ha funcionado a la perfección, incluso con su gráfica me fue suficiente para un entorno de escritorio GNOME con GNU/Linux y he acabado muy contento con la marca Sony al contrario de mi anterior portátil Dell XPS 1530 de la que no pienso comprar nunca nada de esta marca, quizá tuve mala suerte pero el portátil Dell no fue barato, no acabé contento con la calidad del producto pasado el encantamiento inicial y al final acabó totalmente muerto sin ni siquiera encenderse.

Con lo que desde hace unos meses he empezado a barajar opciones para comprar equipo con los requisitos que tengo de ser lo más pequeño posible, silencioso, con 32 GiB de memoria, procesador con mínimo 4 cores y 8 hilos, no necesito tarjeta gráfica dedicada ya que no jugaré a juegos o una integrada con bajar calidad de detalles me servirá a modo ocasional. También quería una pantalla externa de 25” o 27” con resolución de 2K para mayor comodidad y evitar problemas por estar con la cabeza medio agachada durante bastante tiempo. Habiendo pasado 5 años desde mi anterior portátil Sony quiero que el ordenador sea un salto significativo en lo que tenía por eso el requerimiento de los 4 cores, los 32 GiB de memoria y la pantalla 2K. La memoria a pesar del caro precio de la DDR4 en el momento de este artículo. El SSD es la mejor inversión para mejorar el rendimiento que uno puede hacer, más que el procesador. El SSD probablemente elija uno en formato M.2 con interfaz NVMe que tienen una tasa de transferencia 3 veces superior a los SSD con interfaz SATA que ya son rápidos, los NVMe son algo más caros pero no mucho más.

He barajado varias opciones principalmente ordenadores en factor de forma pequeños como los Intel NUC, Gigabyte Brix, Zotac, MSI Cubi, Slimbook ONE, Slimbook PRO2 o AMD Ryzen montando un mini-ITX. Ya estoy casi decidido a lo que compraré y no quiero esperar mucho más a tener equipo propio de cuando se comercialicen y aunque estoy aguantando con un Mac del trabajo comentando Tu con tu macOS yo con mi GNU/Linux. Será con mucha probabilidad un Intel NUC Bean Canyon que se lanzarán en la segunda mitad del 2018.

A pesar de saber que todos los procesadores Intel de la última década está afectados por los importantes problemas de seguridad Meltdown y Spectre que se están mitigando por software, de momento no se ha descubierto una forma de aprovecharlos pero quizá en un futuro se pueda y afecte de forma importante al rendimiento para corregirlos si es que se puede. Pero es que el resto de opciones he ido descartando por otros motivos.

Meltdown Spectre Logotipos de Meltdown y Spectre

El precio que calculo tendrá el Intel NUC Bean Canyon con procesador Intel i5 8250U (4 núcleos, 8 hilos, 6 MiB cache) estará en el momento de su salida entre los 350 y 450€ sin incluir la memoria y el SSD, respecto al modelo con el Intel i3 7100U (2 núcleos, 4 hilos, 3 MiB cache) que cuesta unos 250€ se puede dudar si esos dos núcleos adicionales y 3 MiB de caché más en el procesador merecen la pena la diferencia de precio. En mi caso estoy estoy dispuesto a pagarlo queriendo tener algo significativamente con mejor rendimiento teórico que lo que tenía.

He revisado algunos análisis de los NUC y me han gustado, en este por ejemplo se explica cómo instalar la memoria, el SSD, como es la BIOS/UEFI y sus opciones de configuración como por ejemplo el ring que puede mostrar la actividad del SSD y se pueden personalizar los colores, también muestra el rendimiento en algunos juegos. Y en otro la gráfica HD 620 es suficiente para jugar a Diablo 3 a 30 fotogramas por segundo en un resolución de 1080p bajando detalles, esta gráfica integrada equivale a una dedicada NVIDIA GeForcd 920m laptop o GeForce 730 desktop que no está del todo mal para un jugador ocasional si no importa jugar bajando algunos detalles de calidad. En GNU/Linux es posible jugar a juegos destinados a Windows con PlayOnLinux.

Investigando más he visto también el Intel i5 8259U, solo cambia el último número pero la diferencia es importante en el apartado gráfico ya que este en vez de una UHD 620 lleva una Iris Pro 655 que es nada más y nada menos el doble de potente en teoría gracias a que tiene el doble de execution units, shading units y memoria dedicada de 128 MiB (que la UHD 620 no tiene). No llegará al nivel de una gráfica dedicada de NVIDIA o AMD pero es suficiente para el jugador ocasional.

El uso que le daré es el de programación, virtualización, contenedores Docker, navegación, ofimática, leer PDF, tal vez algún juego ocasional, quizá Diablo 3 si pudiese, y usando GNU/Linux con la distribución Arch Linux. En un futuro lo quiero como equipo servidor dedicado personal con por ejemplo Nextcloud.

Los Gigabyte Brix ya hay algunos modelos con los Intel i5 8250U a un precio de unos 440€, sin embargo, no me gusta que no tienen luz indicador de actividad de SSD y creo que su BIOS/UEFI es en modo texto. La luz de actividad de SSD me parece importante para saber si en algún momento el ordenador se queda bloqueado o por algún tipo de problema lee, o peor escribe, constantemente al almacenamiento persistente cuando no debería. Los Zotac están dotados de una buena cantidad de puertos USB, incluso algunos tiene dos conexiones de red pero son algo caros y la mayoría de sus modelos están orientados a gamers, no son fáciles de encontrar ni siquiera en Amazon ni en PC Componentes que es donde compraré finalmente el equipo. De MSI Cubi no hay muchos modelos y aún no he visto ninguno anunciado con un Intel de 4 núcleos. Los Slimbook ONE me gustaban pero los modelos con los Intel de 4 núcleos solo tienen una ranura de memoria, se les pueden instalar 32 GiB… cuando haya módulos de memoria de 32 GiB, ahora me tendría que conformar con 16 GiB.

También he seguido los NUC con un Intel Core i3 7100U sin embargo tiene solo 2 cores y 4 hilos con 3 MiB de memoria será algo mejor que el Intel i3 3210M que tenía pero no me parece una mejora significativa habiendo pasado 5 años y a pesar de que el 7100U está fabricado a 14 nanómetros y el 3210M a 22 nanómetros, los NUC con Intel i7 8650U son más caros, sobre los 580€ y teniendo en cuenta que la memoria y SSD hay que comprarlos aparte. Los NUC más económicos sobre los 175€ con Intel Pentium J5005 solo admiten 8 gigas como máximo y me parece que están limitados artificialmente a ese tamaño máximo de memoria. En Aliexpress hay productos similares a los NUC con procesador i5 8250U pero a ver quien se la juega a importar desde China por aduanas y con la incertidumbre de lo que llega y que tal funciona, también he estado mirando placas ARM aunque no con el objetivo que sea mi equipo principal, 7+ computadoras baratas del tamaño de una tarjeta de crédito basadas en GNU/Linux y ARM, como la Rock64 que tiene 4 gigas pero siguiendo su desarrollo y foros no son pocos los problemas que la gente crea en su repositorio de Github con lo que me da que pensar que la experiencia podría ser algo frustrante, la arquitectura x86/x64 es mucho más compatible, en ARM cada placa es un mundo aparte de que su rendimiento es mucho más limitado.

Preferiría un microprocesador AMD ya que solo están afectados por algunas variantes de Spectre, pero AMD no tiene tanta diversidad de productos como tiene Intel, no tienen nada parecido a los Intel NUC y en portátiles prácticamente todos los modelos equipan un Intel, quizá cambia ahora algo con los Ryzen. Por rendimiento y precio posiblemente elegiría un AMD Ryzen 2400G pero de momento las placas con el chipset B450 para sacarle el máximo rendimiento aún no están disponibles, en la generación 1xxx de los Ryzen no hay modelos con gráfica integrada, tendría que ser un mini-ITX y el equipo me lo tendría que montar yo, no es difícil pero si tuviese algún imprevisto o incompatibilidad sería un problema por tener que devolver piezas y buscar otras, además no estoy seguro del ruido que haría, lo quiero muy silencioso al menos en tareas ofimáticas, navegación por internet y cosas simples, no estoy seguro de que lo sea con el ventilador de la CPU y fuente de alimentación. Ocupa más que un NUC para mi importante si en un futuro le quiero dar uso como ordenador secundario, servidor personal o quisiera traspasarlo. Pero por 400€ se tiene un ordenador muy decente en plataforma AMD Ryzen a falta de memoria y SSD.

La pantalla que estoy siguiendo es una 2K de BenQ PD2700Q en tamaño de 27”, 4K me parece excesiva resolución para ese tamaño de pantalla, a su máxima resolución en una 27” 4K las cosas ven muy pequeñas y tendría que escalarla a 2K, busco que sea IPS ya que los colores en este tipo de paneles se ven significativamente mejor que en un panel TN, uno al lado del otro la diferencia es clara. Apreciaré una mejora notable en la calidad de la pantalla de la que tenía en el portátil Sony. No es barata pero es algo que no necesitan renovarse tan a menudo como el ordenador seguramente pueda tener un uso durante una década si no presenta ningún problema de funcionamiento. Otros aspectos interesantes son ser regulable en altura y horizontalmente, colocable en posición vertical y puertos USB, HDMI y DisplayPort por otro lado una información en pantalla o OSD aceptable y de fácil configuración.

Entre otras cosas están el teclado y ratón que me conformo con un modelo básico y barato como el Logitech MK120 y una alfombrilla que cubra la mesa.

Otras personas no se lo piensan tanto pero en un área del que más o menos entiendo algo me gusta saber con mucho detalle lo que estoy comprando. Una vez que uno comprende las especificaciones técnicas de los productos, que diferencias hay entre cada una de ellas, se entiende las diferencias de precios y se compra con criterio.

No es lo mismo comprar un NUC7i3BNH con un i3-7100 que tiene una gráfica HD Graphics 620 con un precio de unos 250€ que un NUC7i5BNH con un i5-7260U que tiene un gráfica Iris Plus 640 con un precio de unos 360€.

La diferencia del precio está en que el procesador i5-7260U que tiene 4 MiB de cache por 3 MiB del i3-7100 que le proporciona algo más de rendimiento y la gráfica Iris del primero es el doble de potente con 48 execution units, 384 shading units, con memoria gráfica dedicada de 64 MiB eDRAM para proporcionar unos 700800 GFLOPS de rendimiento contra la gráfica HD Graphics 620 que tiene 24 execution units, 192 shading units, 0 MiB eDRAM (sin memoria dedicada) con unos 400 GFLOPS. Aún siendo ambas gráficas integradas para jugadores ocasionales en todo caso mucho menos capaces que una dedicada con su memoria más veloz, la Iris Plus 640 del i5-7260U es el doble de potente que la HD Graphics 620 del i3-7100. Por este motivo se justifica la diferencia de precio entre los modelos. Dependiendo del uso que se le dé al NUC será preferible un modelo u otro.

En estas dos páginas se pueden conocer muchos detalles de los procesadores y gráficas de Intel.

Cuando realmente compre el equipo publicaré varios artículos con el unboxing, instalación, análisis, uso y opinión personal de varios de ellos.

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

Blog Bitix

Hemeroteca #13

junio 30, 2018 08:00

El primer semestre del 2018 ya ha pasado. Estos primeros meses he publicado 38 artículos sobre la temática habitual de esta bitácora como programación, software libre, GNU/Linux algunos de opinión relacionados con la tecnología. Las visitas e ingresos de AdSense no han crecido tanto como en ocasiones anteriores pero se han mantenido a un nivel similar sobre los 30K páginas vistas y los 30€ de ingresos en AdSense que no están nada mal.

Dado que desde hace unos cuantos meses no tengo ordenador propio y solo dispongo de el del trabajo normalmente solo los fines de semana no he podido investigar, probar tantas cosas que me gustaría y he publicado más artículos de opinión que de software libre y GNU/Linux. Como comento en algunos de los artículos publicados más o menos ya tengo decidido que mi siguiente equipo será un Intel NUC pero estoy esperando a que salgan esta segunda mitad los Bane Canyon para que el salto que experimente sea notable tanto en el microprocesador con un Intel Core i5 8250U o 8259U, 4 núcleos y 8 hilos, 6 MiB de caché y 32 GiB de memoria sobre lo que tenía antes un Intel Core i5 3210M, 3 MiB de caché, 2 núcleos, 4 hilos y 8 GiB de memoria. Con tal cantidad de memoria podré hacer algunas pruebas con contenedores y virtualización que con solo 8 GiB de memoria ya incluso se me quedaba escaso. Cuando adquiera este nuevo equipo publicaré varios artículos con el desempaquetado o unboxing y el análisis de varios de esos componentes.

Evolución Analytics y AdSense

Entre los artículos sobre programación han estado los habituales sobre Java, destacando las novedades de Java 10, un par sobre Apache Tapestry. Como resolver varios de los problemas clásicos de sincronización, programación concurrente en Java, Webjars, …

En la categoría de software libre o GNU/Linux como tener una consola de juegos retro con una Raspberry Pi, varias computadoras similares a la Raspberry Pi.

En la categoría de opinión están el importante problema de seguridad que se hacía público a principio de año y que afectaba a todos los microprocesadores de Intel fabricados desde hace una década con Meltdown. Intel, AMD y ARM afectados otras variantes denominadas Spectre. Como el hardware estos microprocesadores es defectuoso ni solucionable por microcódigo solo se ha podido mitigar mediante parches de software en los nucleos de los sistemas operativos y compiladores que en cualquier caso pueden suponer una merma en el rendimiento. Aún no se conoce un software capaz de aprovechar estos fallos de seguridad pero no quiere decir que no se descubran. Afecta aún más si cabe a entornos de computación en la nube al compartirse los recursos de computación, en estos entornos es especialmente grabe por seguridad ya que el fallo permite leer la memoria del kernel, aplicaciones, contenedores, datos, claves, …

También publiqué un artículo el ordenador con las opciones que barajaba, una opinión sobre el Slimbook AIO Curve y cómo internet está transformando el sector del comercio y distribución o mi opinión de que me quedo GNU/Linux sobre un Mac. También los artículos con las opciones que estoy tomando para comprar equipo ya no a mucho tiempo tardar.

Para finalizar otro artículo fuera de tema del habitual de este blog con los plantones de roble que tengo, como han crecido y como están desde que los planté el año pasado. Aunque aún no lo he publicado algunos están mostrando síntomas de fallecimiento y lo que es peor no tengo claro por que si por la cantidad de agua, excesiva luz directa del sol, humedad, calor, …

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

Adrianistán

¿Cómo funcionan los sistemas basados en inodos?

junio 14, 2018 10:18

Después de ver como funcionan de forma genérica los sistemas FAT, saltamos a los sistemas de inodos. Estos se han usado tradicionalmente en sistemas UNIX (UFS, ext2), así que tradicionalmente ha existido una cierta rivalidad  en las redes entre FAT e inodos similar a la de Windows/Linux. Lo cierto es que a nivel técnico cada uno tiene fortalezas y debilidades.

Partición

Tomando la base de FAT, una partición de un sistema basado en inodos también contiene un sector de arranque y un superbloque con metadatos. También es necesario un bloque dedicado al directorio raíz presente en el disco. Además es necesario espacio para almacenar todos los inodos y un mapa de bits de espacio libre que en FAT no hacía falta, ya que la propia tabla nos indicaba que bloques del disco estaban libres.

Los inodos

¿Qué es inodo te podrás preguntar? Es una estructura de datos, su nombre proviene de index node y es que los inodos no son más que índices, que almacenan los números de bloque de las diferentes partes del archivo. Además, contienen metadatos como permisos, propietario, tamaño, fecha de modificación, referencias, tipo de fichero (directorio, archivo, enlace duro, enlace blando,…) salvo el nombre del propio archivo, que en ningún sitio del inodo aparece.

Este sistema tiene una ventaja de rendimiento respecto a FAT en cuanto al acceso aleatorio a los archivos, ya que es mucho más rápido de esta forma que con FAT. En FAT para hacer lo mismo tenemos que ir recorriendo la tabla arriba y abajo siguiendo los números de bloque hasta encontrar el bloque deseado.

Normalmente un inodo tiene un tamaño fijo, lo que implica que el índice no se puede alargar hasta el infinito. Esto hace que haya un número limitado de bloques que puede usar un archivo y por ende, que haya un tamaño máximo de archivo que no es muy elevado. Para solventar este problema hay varias soluciones. El enfoque de UFS y de ext2/3/4 consiste en múltiples niveles de indexado progresivo.

Esto significa que los primeros bloques son números de bloque directos pero los siguientes son números de bloque que llevan a tablas de inodo secundarias que ya sí, hacen referencia al archivo real. Más adelante los números de bloque hacen referencias a tablas de inodo secundarias que a su vez llaman a tablas de inodos terciarias.

Esto provoca algo que en principio puede parecer paradójico y es que es más lento leer una zona final de un archivo que una zona del principio, aunque en una lectura secuencial no se nota demasiado.

Otra solución a esto es enlazar los inodos a modo de lista enlazada añadiendo al final de cada inodo un número de inodo al que continuar la lectura de índices.

Localización de los inodos

Los inodos se crean cuando se formatea el disco y permanecen en un estado libre. No se pueden añadir más inodos ni quitar inodos y no puede haber más archivos y directorios que inodos por esta misma razón. Esto es una desventaja respecto a FAT, ya que en FAT puede haber tantos archivos como bloques haya/entradas tenga la tabla. En sistemas de inodos como ext2/3/4 puede ocurrir que no queden inodos libres pero haya todavía bloques libres, dejando todo ese espacio inutilizado (aunque en realidad lo podrían usar los archivos existentes si creciesen).

Los inodos se pueden ubicar de dos formas distintas. Un enfoque consiste en ponerlos al principio del disco todos juntos. Otro enfoque, el que sigue ext3, consiste en dividir el disco en 4 zonas y ubicar inodos en cada inicio de zona. La idea es que los inodos de esa zona usen bloques de esa zona y de esta forma reducir los desplazamientos de las cabezas lectoras del disco (en unidades SSD esto da completamente igual como podréis suponer).

Gestión del espacio libre

Una de las grandes ventajas de FAT era que la tabla podía mantener a la vez un listado de bloques libres, listos para ser usados. Con inodos no tenemos esa facilidad y tenemos que recurrir a otros tipos de estructura. Aquí hay muchos planteamientos siendo el más común el mapa de bits. El mapa de bits es una estructura que se compone de un listado de bits. Cada bit corresponde a un bloque y dependiendo de si el bit es 1 o 0 podemos saber si el bloque está libre u ocupado.

Como veis, los sistemas basados en inodos tienen que mantener varias estructuras de datos de forma paralela. Esto aumenta las probabilidades de inconsistencias en el sistema. Por este motivo, muchos sistemas más avanzados como ext4 mantienen un journal o diario.

Fragmentación

Veamos como se comportan los sistemas de inodos ante los tres tipos de fragmentación. Respecto a la fragmentación interna, que es aquella que sucede cuando asignamos espacio de más a archivos que no necesitan tanto, tiene los mismos valores que FAT, ya que los bloques siguen pudiendo pertenecer únicamente a un archivo. Realmente, para mejorar la fragmentación interna tenemos que saltar a sistemas como XFS o JFS.

La fragmentación externa de los sistemas basados en inodos es la misma que la de FAT, 0, ya que todo se asigna mediante bloques del mismo tamaño.

Respecto a la fragmentación de datos, históricamente las implementaciones como UFS o ext2/3 se han comportado relativamente bien, aunque a nivel teórico nada impide que se comporten igual de mal que FAT, no existen mecanismos preventivos. Ext4 por contra, sí que tiene mecanismos de prevención (delayed allocation).

Directorios y enlaces duros

En los sistemas basados en inodos los directorios son más sencillos que en FAT, ya que no tienen que almacenar los metadatos del archivo en cuestión. En un fichero de directorio simplemente se almacena el nombre y el número de inodo correspondiente.

Aquí surge un concepto muy interesante, el de enlaces duros. Un enlace duro no es más que otra entrada de directorio que apunta a un mismo inodo. Realmente desde el punto de vista del sistema de archivos no existe un fichero original y su enlace. Simplemente dos entradas diferentes apuntando al mismo inodo. ¿Y cómo reacciona esto a los borrados? Imagina la siguiente situación: tengo un fichero y creo un enlace duro. Borro el fichero original y su inodo se libera. Ahora creo otro archivo y reutiliza ese mismo inodo que estaba libre. ¡Ahora el enlace duro apunta a un contenido totalmente distinto sin darse cuenta! Para prevenir esto, los inodos no se liberan hasta que no quede ninguna entrada de directorio apuntando a ellos. Para eso sirve el campo referencias dentro del inodo, para llevar la cuenta de cuántas veces se hace referencia al inodo en el sistema de archivos. Cuando el valor de referencias llega a cero, se puede liberar sin problema.

Conclusiones

Los sistemas basados en inodos son conceptualmente algo más complejos que los basados en FAT. Comparten además limitaciones comunes, usan más espacio de disco, aunque suelen ser más rápidos.

Actualmente existen sistemas basados en inodos mucho más avanzados y complejos que UFS y ext2/3/4 y que mejoran este sistema de forma sustancial. Por ejemplo en XFS los inodos tienen una estructura totalmente distinta donde se indica un bloque de inicio y una longitud en número de bloques contiguos que pertenecen al archivo. Muchos sistemas además usan estructuras de árbol B+ como pueden ser ZFS o Btrfs.

La entrada ¿Cómo funcionan los sistemas basados en inodos? se publicó primero en Adrianistán.

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

Adrianistán

¿Cómo funcionan los sistemas de archivos basados en FAT?

junio 11, 2018 06:18

Voy a dedicar unas entradas en el blog a hablar del funcionamiento de los sistemas de archivos, un componente fundamental en la gran mayoría de sistemas informáticos. Voy a empezar con los basados en FAT sin centrarme en ninguno en concreto (FAT16, FAT32, exFAT,…). Intentaré escribir también sobre inodos y mecanismos más raros y avanzados.

En esencia un sistema de archivos es un método ordenado que permite guardar datos sobre un soporte físico para luego poder acceder a ellos. Históricamente ha habido muchos enfoques a este problema: los sistemas más usados usan archivos, directorios y enlaces.

Bloques y sectores: la división del disco

Esta parte es común a muchos sistemas de archivos, tanto FAT como inodos, como otros. A nivel físico los dipositivos están divididos. En el caso del disco duro, el dispositivo de almacenamiento más común, los discos se dividen en sectores de N bytes, según parámetros de la fabricación. Estos sectores cuentan con código de control de errores incorporado y todo ello es manejado por la controladora de disco que opera ajena al sistema operativo. Los sistemas de archivos dividen a su vez el disco en bloques. Es importante dejar claro que bloque no es igual a sector. El bloque es una división que hace el sistema de archivos y los sectores los hace la controladora de disco. Usualmente un bloque equivale a varios sectores, aunque la equivalencia real depende del sistema de archivos en concreto.

Algunos bloques especiales: boot y superbloque y raíz

Antes de entrar en el mecanismo específico de FAT es necesario comentar que en un disco existirán dos bloques de vital importancia ajenos a FAT. El primero es el bloque de boot o arranque (también llamado MBR). Normalmente situado en el sector 0 del disco duro, contiene código para iniciar el sistema operativo. El superbloque suele ser el bloque siguiente, contiene metadatos del sistema de archivos (por ejemplo, puede decir que usamos FAT con bloques de 32KiB, etc). Además en FAT es necesario que exista un fichero siempre, el fichero del directorio raíz.

Directorios

Los directorios o carpetas son archivos como otros cualquiera, solamente que en sus metadatos se indica que es un directorio y no un fichero corriente. Los ficheros de directorios son los encargados de almacenar los metadatos de los ficheros (paras saber si son ficheros, otros directorios, su fecha de modificación, propietario y tamaño entre otros) que se encuentran en el directorio así como una referencia al bloque.

Un fichero en FAT

Vamos al asunto. Supongamos que en un determinado bloque N tenemos un fichero. Este bloque es de 64 KiB. Si un fichero ocupa menos de 64 KiB, perfecto, todos los datos entran en el bloque. Simplemente ajustamos los metadatos de tamaño con el valor correcto y el resto del espacio que queda en el bloque queda inutilizado.

Este espacio perdido se denomina fragmentación interna y dependiendo de los datos que se almacenen en un disco duro, el porcentaje de pérdida puede ser mayor o menor. Evidentemente si tenemos bloques muy grandes y ficheros muy pequeños perderemos mucho espacio debido a la fragmentación interna. Tener bloques muy pequeños y ficheros muy grandes también es problemático pero por otros motivos.

Tipos de fragmentación

En un sistema de archivos existen 3 tipos de fragmentación: interna, externa y de datos. La interna se refiere al espacio perdido en bloques asignados a ficheros que no están llenos por completo. La externa se refiere al espacio que perdemos por no tener un espacio libre contiguo lo suficientemente grande como para guardar el fichero allí. Ningún sistema FAT o de inodos tiene fragmentación externa al usar todos bloques de tamaño predefinido. Por último la fragmentación de datos, o fragmentación a secas, se refiere a que los bloques asignados estén contiguos o no. Esto tiene implicaciones a nivel de rendimiento pero no al número de bytes que se vuelven inútiles como los otros dos tipos de fragmentación.

¿Pero qué pasa si el contenido de nuestro fichero no puede entrar en el tamaño de bloque? Aquí viene la gracia de FAT, la File-Allocation-Table. La FAT es una tabla compuesta por entradas que indican el siguiente bloque del archivo. La tabla está indexada por bloque y además de indicar cuál es el siguiente bloque del archivo también indica si el archivo acaba ahí o si ese bloque está libre y puede usarse para un archivo nuevo.

En la foto el archivo /home/user/hola.txt tiene una longitud menor al tamaño de bloque. Así que miramos en la FAT la entrada 150 y efectivamente comprobamos que no hay bloque siguiente ya que es un End-of-File.

Pongamos un ejemplo con un archivo largo. Cada celda de la tabla correspondiente al índice del bloque actual indica el siguiente bloque a leer. Estos bloques pueden estar en cualquier parte. Si en un disco duro muchos ficheros tienen los bloques muy dispersos, se dice que necesita ser desfragmentado.

Conclusiones

FAT es muy rápido si la tabla FAT consigue ser cargada en memoria, ya que obtener los datos necesarios de ella será muy rápido. Desgraciadamente, dependiendo del tamaño del disco duro y del tamaño de los bloques, esta tabla puede ocupar mucho y ser impráctico.

El mecanismo FAT como vemos es simple pero efectivo. Dispone de una fragmentación interna limitada y carece de fragmentación externa. Un inconveniente es que FAT cuando busca un archivo necesita empezar siempre desde el directorio raíz. Esto implica accesos cada vez más lentos a medida que vayamos haciendo carpetas, aunque con sistemas de caché se puede reducir.

El esquema FAT tampoco impone restricciones propiamente dichas al número archivos de un sistema ni a su tamaño. Sin embargo en la práctica suelen existir límites.

Edito: He explicado mejor que significa la fragmentación y sus tipos

La entrada ¿Cómo funcionan los sistemas de archivos basados en FAT? se publicó primero en Adrianistán.

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

Picando Código

Lanzamiento ATuNombre.uy 2.0 👩👧👱👵

junio 11, 2018 12:00

DATA presenta una versión renovada de ATuNombre: la aplicación que señala el problema de la desigualdad de género en el nomenclator urbano:

ATuNombre.uy

Hoy les escribimos para contarles del re-lanzamiento, en una versión completamente renovada, de ATuNombre.uy. Un proyecto que toma los nombres de las calles de Montevideo, para hablar sobre la desigualdad de género.

El objetivo de este mapa es dar visibilidad a los aportes que hacen las mujeres a nuestra sociedad, promover su visibilidad y reconocimiento público a través de los nombres de calles de Montevideo. Queremos ver en qué medida estos aportes son valorados, reconocidos y qué hacían esas mujeres para que “pasen a la historia” otorgándoles el nombre de una calle, pasaje o avenida.
¿Cómo lo hicimos?

Este proyecto nace de una propuesta de DATA Uruguay, que invitó a la comunidad a desarrollar esta idea a través de nuestros encuentros regulares llamados Café de DATA (reuniones para trabajar en Datos Abiertos y herramientas sociales).

La primera versión de este sitio se comenzó a trabajar junto a un equipo voluntario (¡gracias!) el 21 de febrero de 2015 (Día Mundial de los Datos Abiertos) y en 2017, empezamos a trabajar nuevamente en renovar la propuesta a lo largo de varios encuentros y con trabajo remoto. Conocé al equipo que hizo esto posible y tené en cuenta que este proyecto va a seguir desarrollándose y mejorando.

» 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

Sponsors

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