Weblogs Código

Variable not found

Logging en ASP.NET Core

May 04, 2016 07:10 AM

ASP.NET CoreUno de los requisitos más habituales que tenemos en cualquier aplicación medianamente compleja es la creación de logs donde guardar información cuando ocurran determinados eventos.

En ASP.NET "clásico" teníamos varias opciones, como utilizar las propias herramientas de tracing del framework o frameworks especializados de terceros como los célebres Log4Net, NLog o muchos otros, pero al final la idea era la misma, poder disponer de un componente en el que poder registrar sucesos como el siguiente:
public ActionResult RemoveInvoice(string id)
{
... // Remove the invoice 
    _logger.LogInformation($"Invoice {id} was removed by {User.Identity?.Name}");
return RedirectToAction("Index");
}
El nuevo ASP.NET Core viene equipado de serie con un sistema de logging interno, integrado en el propio marco de trabajo, que proporciona funcionalidades básicas pero, sobre todo, los mimbres básicos sobre los que montar sistemas de trazas más complejos y personalizables.

Dedicaremos este post a ver cómo se configura y utiliza este mecanismo que, como otros aspectos del framework, está construido pensando en su extensibilidad, y hace uso intensivo del sistema de inyección de dependencias.

Nota: aunque este componente no es un gran candidato a cambiar dada las pocas actualizaciones que ha sufrido en los últimos meses, es bueno recordar que todavía estamos utilizando una release candidate de ASP.NET Core y, por tanto, algunos detalles aún podrían variar antes de que aparezca la versión final del producto.

1. Creación y uso básico de un logger

Como comentaba algo más arriba, el sistema de logging hace uso de las herramientas internas de inyección de dependencias, y esto es algo que queda patente desde su configuración. De hecho, para configurar un logger lo primero que haremos es obtener una instancia de ILoggerFactory en algún punto inyectable, como el método Configure() de la clase de inicialización:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddDebug();
...
}
ILoggerFactory es una factoría de loggers, es decir, un componente cuya misión es crear objetos ILogger que son los que realmente utilizaremos para registrar nuestros eventos, utilizando para ello una serie de proveedores configurables.

Por ejemplo, la llamada a AddDebug() que hemos visto anteriormente configura la factoría para que los loggers que sean creados desde ella utilicen como canal de salida la ventana de resultados de depuración de Visual Studio. Es decir, desde nuestro código registraremos eventos en el log, y los veremos aparecer por esta ventana de nuestro entorno de desarrollo.

Pero tenemos otras opciones para configurar a dónde irán a parar las trazas. Igual que antes hemos utilizado AddDebug(), tenemos también disponible AddConsole() para hacer que los mensajes aparezcan en la ventana de consola (útil cuando ejecutamos la aplicación web desde línea de comandos), que vayan directamente al registro de evento de Windows (obviamente esta opción sólo estará disponible sobre .NET 4.x en Windows), o crear nuestros propios destinos personalizados implementando proveedores personalizados, como de hecho ya existen para NLog, Loggr, Elmah.io, o Serilog.

Incluso podemos configurar nuestra factoría con más de uno de ellos para que los eventos se registren en distintos puntos.
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{     loggerFactory.AddDebug();
    loggerFactory.AddConsole();
    // Los eventos logados aparecerán en la consola y en la ventana de VS 
    ...
}
Bien, una vez configurada la factoría, el siguiente paso del proceso sería crear el objeto ILogger que usaremos para registrar eventos en los canales que hayamos configurado. Esto lo hacemos mediante el método CreateLogger() de ILoggerFactory, al que debemos suministrar un nombre de categoría.

Este nombre se suele utilizar para indicar el componente origen del evento, pero en realidad podemos utilizar cualquier string que nos ayude a puntualizar el contexto en el que ha sido registrado:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddDebug();
ILogger logger = loggerFactory.CreateLogger("Startup");    

    // Usar logger para registrar eventos:
...
}
También podemos utilizar una versión genérica de CreateLogger(), que tomará el nombre de la categoría del tipo que usemos en su invocación:
    ILogger logger = loggerFactory.CreateLogger<Startup>();
En cualquier caso, tras ello ya lo único que tendríamos que hacer invocar el método LogInformation() del ILogger pasándole el mensaje a registrar:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddDebug();
ILogger logger = loggerFactory.CreateLogger<Startup>();
    logger.LogInformation("Application starting");

...
}
Observad que dado que la factoría ha sido configurada para que los loggers usen la ventana de depuración de Visual Studio, al ejecutar la aplicación veríamos en ella algo similar a lo siguiente:

Log en ventana de depuración de Visual Studio

2. Niveles de traza

Por otra parte, como podríamos esperar, a la hora de registrar eventos en el log, tenemos también distintos niveles mediante los cuales indicamos la importancia de los mensajes y que pueden ser utilizados a posteriori para filtrar las trazas y evitar el exceso de información. ASP.NET Core define los siguientes niveles en la enumeración LogLevel:
  • LogLevel.Debug, el nivel más detallado con todo tipo de información técnica destinada a desarrolladores. Pueden contener información sensible, por lo que por defecto está deshabilitado y no se recomienda su activación en entornos de producción.
  • LogLevel.Verbose, para mensajes de depuración sobre cambios o estado puntual de un componente, pero sin utilidad en el largo plazo.
  • LogLevel.Information, destinado a mostrar mensajes que indican el flujo de ejecución de una aplicación.
  • LogLevel.Warning deberíamos usarlo para registrar acontecimientos anormales en el flujo de ejecución que no paran el sistema pero que deberían ser estudiados, como puede ser la captura de una excepción controlada.
  • LogLevel.Error debería usarse para registrar errores en la ejecución (por ejemplo, excepciones no controladas) que abortan un proceso, pero que no impiden que la ejecución de la aplicación continúe.
  • LogLevel.Critical, reservado para errores críticos que requieren atención inmediata, como que un disco se haya quedado sin espacio o se haya perdido de forma permanente la conexión con la base de datos.
Extensores de ILogger para registrar eventosPara cada uno de estos niveles encontraremos un método extensor en ILogger, de forma que podremos utilizarlos de forma muy sencilla y cómoda a la hora de registrar eventos: LogDebug(), LogVerbose(), LogInformation(), etc.

Para evitar el exceso de ruido, a la hora de configurar los proveedores que vamos a utilizar para almacenar los logs podemos indicar el nivel mínimo de logging que soportará, de forma que todos los registros que se encuentren por arriba serán ignorados. Por ejemplo, si añadimos la siguiente línea a la configuración, configurará la factoría para que los loggers usen la ventana de depuración de VS, pero en ella sólo se muestren los eventos de tipo Warning, Error o Critical:
loggerFactory.AddDebug(minLevel: LogLevel.Warning);
Fijaos que esto, unido al hecho de que podemos configurar varios proveedores en la factoría, puede posibilitarnos escenarios muy interesantes, como registrar eventos en distintos orígenes en función del nivel de traza, o incluso de otros criterios más sofisticados.

3. Simplificar la creación del logger

Habréis observado que crear el logger desde cualquier punto de nuestra aplicación y usarlo sería bastante sencillo porque, una vez configurada, sólo habría que obtener esa instancia de ILoggerFactory , crear el objeto ILogger a partir de ella, y usarlo para registrar los eventos, como en el siguiente ejemplo:
public class HomeController : Controller
{
private readonly ILoggerFactory _loggerFactory;

public HomeController(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}

public IActionResult Index()
{
var logger = _loggerFactory.CreateLogger("HomeController");
logger.LogInformation("Index action executed");
return View();
}
}
Este código podemos simplificarlo un poco, a la vez que ganamos en organización, si utilizamos el servicio de inyección de dependencias para obtener directamente una instancia de ILogger para la clase actual:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}

public IActionResult Index()
{
_logger.LogInformation("Index action executed");
return View();
}
}
Logging en la ventana de salida de Visual Studio
Y lo dejamos aquí, al menos de momento ;) Espero que el post os haya resultado útil para ver el funcionamiento básico del sistema de logging de ASP.NET Core y sus posibilidades. Más adelante a ver si me animo y vemos cómo crear un proveedor de log personalizado, por ejemplo para guardar las trazas en un buffer  en memoria o quizás en una base de datos, o tal vez enviarnos por email los errores críticos… las posibilidades, como siempre, infinitas :)

Publicado en Variable not found.

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

Picando Código

Las universidades hispanoamericanas y españolas que más apoyan el software libre en 2016

May 03, 2016 12:00 PM

El Observatorio Tecnológico PortalProgramas volvió a publicar -ya en su 5ª edición- el Ranking de universidades en Software Libre – RuSL 2016. Es un estudio que se ocupa de medir el compromiso, uso y difusión del software libre en 144 universidades latinoamericanas de un total de 18 países y 76 universidades españolas.

Este año, la redacción del prólogo del RuSL cuenta con la participación de Antonio Monje Fernández, director del Centro Nacional de Desarrollo Curricular en Sistemas No Propietarios (CeDeC), coordinador de los desarrollos de la herramienta exelearning.net, autor (entre otras publicaciones) de la Guía de creación de recursos educativos abiertos, y pionero en el uso y la difusión de Moodle para la educación secundaria, así como de las nuevas tecnologías basadas en Software Libre.

Entre las conclusiones más interesantes de la clasificación hispanoamericana del RuSL 2016 se pueden destacar:

  1. En Cuba, como en Venezuela, el software libre y la cultura de código abierto son, oficialmente, elementos clave en su camino hacia la soberanía tecnológica. El apoyo institucional demuestra que es fundamental para que el software libre se divulgue rápidamente.
  2. La divulgación, por medio de eventos relacionados con el software libre, es una de las dimensiones de este ranking que muestra los resultados más positivos.

Resultados Universidades Hispanoamericanas

Para esta segunda edición del ranking de universidades latinoamericanas se ha ampliado la lista de instituciones analizadas hasta llegar a las 144 universidades, más del doble que las observadas en el año anterior, y con la presencia de un país que quedó ausente en la primera edición: Honduras. Como ocurrió en la investigación correspondiente a la edición de 2015, las universidades brasileñas no han sido valoradas por motivos lingüísticos.

Las universidades hispanoamericanas que más apoyan el software libre en 2016

Se destaca la Universidad de las Ciencias Informáticas (UCI), en Cuba en casi todas las dimensiones del ranking, con especial incidencia en la producción de software libre y la colaboración en proyectos relacionados con la promoción de la cultura open source. La querida Universidad de la República de Uruguay (UDELAR) ha obtenido mejor puntuación en los criterios de divulgación.

Pueden ver más conclusiones y datos interesantes sobre las universidades estudiadas en el sitio del estudio.

Resultados Universidades Españolas

Los resultados muestran como continúa la tendencia al alza observada en las anteriores ediciones (2012, 2013, 2014 y 2015) y visible a través del IDSL (Índice de Difusión del Software Libre), que, aunque muy discretamente, aumenta cada año su valor medio respecto al curso anterior.

Las universidades españolas más comprometidas con el Software Libre en 2016

Pueden ver más información en este enlace. Se comenta de las universidades en los primeros puestos, así como el crecimiento que ha tenido el tema y algunos datos por comunidad autónoma.

Sobre el estudio y su metodología

Como viene siendo habitual, la elaboración de este ranking ha sido posible gracias al trabajo de un equipo multidisciplinar formado a tal efecto, que ha contado con expertos y colaboradores encargados de definir los criterios e indicadores en los que se basa el RuSL. Gracias a la voluntad de perfeccionar estos criterios y dotarlos del máximo rigor posible, así como a la colaboración de instituciones universitarias que nos ayudan a obtener los datos que conforman esta clasificación, podemos observar como los resultados son cada vez más fidedignos y nos permiten visualizar una radiografía cada vez más clara del impulso que se le da a la cultura del Software Libre desde las universidades españolas.

Todos los datos analizados, junto a las cuestiones relativas a la metodología de trabajo, se han publicado bajo licencia Creative Commons Reconocimiento 3.0, de modo que puedes consultar todos los criterios empleados, distribuidos en las once dimensiones en las que se estructura este ranking.

Ver Las universidades hispanoamericanas y españolas que más apoyan el software libre en 2015.

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

Variable not found

Enlaces interesantes 239

May 03, 2016 07:05 AM

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

.Net

ASP.NET

ASP.NET Core

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Otros


Publicado en Variable not found

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

Ingenieria de Software / Software Engineering / Project Management / Business Process Management

IoT Plarform

May 01, 2016 11:20 PM

https://www.linkedin.com/pulse/comparing-11-iot-development-platforms-dr-elvin-prasad

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

proyectos Ágiles

Master en Métodos Ágiles (MMA)

May 01, 2016 03:10 PM

En octubre de 2016 se iniciará el Barcelona la primera edición del Master en Métodos Ágiles (MMA) en La Salle (Universitat Ramon Llull). Tras 5 exitosas ediciones del formato anterior en forma de Postgrado, y dada la cantidad de conocimiento y áreas en las que se han ido extendiendo los principios ágiles, se han ampliado contenidos y profesorado para transformarlo en Máster, el primero a nivel mundial sobre Agile.

MMA-banner

Será una oportunidad única para aprender de profesionales de primer nivel, con varios años de experiencia específica en Agile, aplicando principios y métodos ágiles en contextos diversos, especializándose en aspectos concretos,  investigando sobre nuevas técnicas y ponentes en conferencias nacionales e incluso internacionales.

Asignaturas Temas Profesores
Fundamentos & Inception Principios y métodos más conocidos (Scrum, Lean, Kanban y XP). Facilitadores e impedimentos. Inception y conceptualización ágil de proyecto. Xavier Albaladejo
Agustín Yagüe
Scrum Estimación y planificación ágil, framework de Scrum, retrospectivas, métricas ágiles, herramientas ágiles físicas, radiadores de información. Xavier Quesada
Silvia Sistaré
Personas y equipos Gestión de personas, gestión de conflictos, motivación e incentivos, facilitación compartida, contratación ágil, coaching de equipos, liderazgo. Jorge Uriarte
Joserra Díaz
Ingeniería Ágil User eXperiencie y prototipado en Agile, ALM ágil, eXtreme Programing, testing ágil, arquitectura ágil, arquitectura empresarial ágil, DevOps. Rodrigo Corral
Pablo Gómez
Ángel Viudez
Marc Pifarré
TDD, BDD, Legacy code Desarrollo guiado por pruebas (de aceptación y unitarias), cómo trabajar con código heredado y reducir la deuda técnica. Carlos Ble
Rubén Bernárdez
Agustín Yagüe
Enterprise Learning & personal efficiency Comunidades de Práctica, Open Spaces, Talent development, gamification, Productividad y aprendizaje personal en Agile. Steven Wallace
Manuel Lopez
Vanesa Tejada
Kanban, Lean & continuous improvement Kanban, Lean Thinking, Lean Software Development, Mejora Continua en Agile Ángel Medinilla
Ivan Font
Joserra Díaz
Cultura y Agile Management Tipos de cultura empresarial, gestión del cambio organizativo, Agile Management. Jasmina Nikolic
Joao Gama
Gabriel Prat
Lean Startup y Enterprise Product Management Lean Startup, innovación, Design Thinking, Impact Mapping, lanzamiento de startups ágiles, Product Portfolio Management, empresas distribuidas. Gabriel Prat
Ángel Díaz-Maroto
Mattijas Larsson
Scaling & rollout Escalado (LESS, Spotify, Nexus, SAFe), desescalado y auto-organización empresarial (reinventing organizations, holocracy, …), contratos ágiles. Adrian Perreau
Fernando Palomo
Xavier Albaladejo
Trabajo Final de Máster Durante el Máster se elaborará un caso práctico de introducción y aplicación de Agile en una empresa, considerando la parte de transformación de métodos y cultura

Algunos comentarios de los alumnos en ediciones anteriores en forma de Postgrado: “Cuando se acaba la clase, estamos ya esperando a que llegue la semana que viene para ver un nuevo tema con el siguiente profesor”. “Muy práctico”. “La calidad y diversidad de puntos de vista entre los profesores le aporta mucho valor”.  Más detalles en: Mejora de la situación laboral los alumnos del PMA trans un año.

Además de los conocimientos concretos que se reciben, uno de los principales cambios que han experimentado los alumnos es mayor perspectiva en los problemas, cómo abordar la complejidad en las empresas, en su trabajo y en las relaciones entre personas y en equipos. Han ganado discurso y aplomo para defender de manera más objetiva propuestas de cambio y mejora.

El Máster tendrá una duración de un año académico y se realizará viernes tarde y sábado por la mañana.

Para más detalles, consultar la página oficial del Máster.

–> Ver también cuál ha sido la Mejora de la situación laboral de los alumnos tras un año y los Principales aspectos valorados por los alumnos.

 


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

Ingenieria de Software / Software Engineering / Project Management / Business Process Management

Marketing de proximidad

April 30, 2016 11:13 PM

http://wpdesign.mx/index.php/2016/04/12/10-casos-de-exito-de-marketing-de-proximidad-en-retail/

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

Blog Bitix

El componente Grid de Apache Tapestry

April 30, 2016 10:00 AM

En la mayoría de aplicaciones no solo es habitual sino algo muy usado el mostrar listados de elementos de forma tabular con paginación y columnas ordenables. En estos listados el complejo componente internamente Grid de Apache Tapestry pero a la vez muy sencillo de usar puede marcar una diferencia significativa en el número de líneas de código necesarias a escribir, la flexibilidad, funcionalidad ofrecida, la productividad al hacer la implementación o modificarla comparándolo con lo necesario en otros frameworks web en los que no hay nada comparable de serie.

Apache Tapstry

Apache Tapestry es uno de los muchos frameworks disponibles en Java para el desarrollo de aplicaciones y páginas web. A diferencia de la mayoría se basa en componentes y proporciona una larga lista de ellos listos para usar de serie, pero también se pueden crear componentes nuevos basados en los propios de Tapestry o los que desarrollemos nosotros muy fácilmente. Los componentes son piezas reusables de código que se pueden reutilizar bien directamente o para formar nuevos componentes y es uno de los motivos por los que en Tapestry se consigue una alta productividad además de otros beneficios como la encapsulación.

Uno de los componentes más complejos pero al mismo tiempo muy simple de usar ofrecidos por el framework es el componente Grid. El componente Grid muestra en una tabla un listado de datos ofreciendo las funcionalidades de paginación, ordenación, personalización de columnas, filtrado de columnas, personalización en caso de estar vacío y algunas cosas más. Lo único que debemos tener en cuenta para aprovechar al máximo el componente Grid son los parámetros que declara en su documentación su funcionamiento interno nos es irrelevante, será de los componentes más complejos y no por ello no es más difícil de utilizar basta decir que solo tiene un parámetro requerido y que es lo único imprescindible que es la lista de datos a mostrar.

Aunque el componente tiene un buen número de parámetros para personalizar según queramos su comportamiento basta que hagamos uso únicamente del parámetro source que es la fuente de datos del Grid, puede ser un objeto de tipo Collection o un GridDataSource que proporciona métodos para hacer la paginación y ordenación eficientemente recuperando de la base de datos o fuente de datos únicamente los registros a mostrar realizando paginación.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/cddb3bbca6ad09c4d602b7b252ef0e35/raw/ProductoAdmin.tml">ProductoAdmin.tml</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/cddb3bbca6ad09c4d602b7b252ef0e35/raw/ProductoAdmin.java">ProductoAdmin.java</pre></a></noscript>
Componente Grid de Tapestry
  • source

Con los parámetros include y exclude podemos determinar que propiedades de los objetos o beans de la fuente de datos se incluyen en el Grid, con el parámetro add podemos añadir nuevas columnas y personalizarlas con los datos que necesitemos como sería el caso de añadir una columna con un Checkbox por fila para realizar una selección múltiple o de una columna con botones para realizar acciones. Para ambas cosas en el cuerpo del componente_Grid_definimos subcomponentes con la siguiente nomenclatura <p:[nombreColumna]Cell>, en en ejemplo usando <p:nombreCell> y <p:actionCell>. Las celdas de las columnas por defecto hacen un toString() de la propiedad del bean de la fila a mostrar, si queremos cambiar este comportamiento como en la columna nombre definimos la etiqueta <p:nombreCell> y dentro incluimos el contenido que deseemos que puede contener otros componentes en este caso el nombre con un enlace.

  • include
  • exclude
  • add
  • <p:[nombreColumna]Cell>

Con el parámetro rowsPerPage podemos cambiar el número de filas por página del Grid, en el ejemplo son 2 pero puede ser la cifra que deseemos y tampoco tiene por que ser una constante, el número de filas a mostrar puede provenir de una expresión y cambiar según alguna lógica. Los parámetros columnIndex, rowIndex y row nos proporcionan información del índice de la columna actual, índice de la fila actual y el objeto actual de la fila respectivamente que podemos usar al personalizar las celdas del Grid. Son parámetros de salida que el Grid se encarga de proporcionarnos según procesa las filas y celdas, en base a ellos podremos implementar alguna funcionalidad.

  • rowsPerPage
  • columnIndex
  • rowIndex
  • row

Los parámetros informales (denominados así para aquellos que le pasamos al Grid que no están declarados explícitamente en su interfaz o contrato y que no proporcionan alguna funcionalidad) son incluidos en la etiqueta table del HTML que genera el Grid. Igualmente el parámetro informal class se incluye tal cual se indica en el atributo en class de la tabla para personalizar los estilos y usando el parámetro rowClass se incluye en cada fila en su etiqueta tr de HTML. Además de estas clases que podemos el componente añade algunas clases más a ciertas filas: t-first para la primera fila, t-last para la última, t-sort-column-ascending y t-sort-column-descending para las columnas que estén ordenadas ascendentemente y descendentemente de forma que con CSS tengamos la posibilidad de cambiar sus estilos.

  • class
  • rowClass

Con el parámetro empty definimos un componente Block que se usará cuando el Grid no tenga filas que mostrar, lo que es útil para mostrar un mensaje indicando que la tabla no tiene filas como cuando no hay elementos.

Mensaje de un Grid sin elementos
  • empty

Con pagerPosition indicaremos si queremos la barra de paginación situada encima de la tabla, abajo, en ambas posiciones o no queremos.

  • pagerPosition

Por si fuera poco con el parámetro inPlace podemos hacer que la paginación y ordenación funcione usando AJAX de modo que no se recargue toda la página en cada pulsación de un enlace. No será necesario que añadamos nada de JavaScript, el componente se encargará de hacer la petición AJAX y con el resultado que sea devuelto actualizar la tabla.

  • inPlace

Finalmente, comentaré el parámetro encoder con el que podemos hacer que el componente Grid funcione cuando se usa dentro de un componente Form. La clase ValueEncoder transforma un objeto a un String que lo identifique en el cliente y a partir del identificador del cliente los transforme al objeto cuando se envíe de nuevo al servidor. Podemos indicar el ValueEncoder en cada Grid o definirlo como una configuración del contenedor IoC.

Hay algún parámetro más como sortModel y paginationModel para mantener la información de ordenación y paginación pero los anteriores son los que más habitualmente usaremos y probablemente con source, include, exclude y add tengamos suficiente para muchos casos.

Es sorprendentemente lo sencillo que es usar el componente Grid para toda la funcionalidad que proporciona. En ciertos casos este componente por si solo puede reducir drásticamente la cantidad de código necesario a escribir en las plantillas que producen HTML y aumentar notablemente la productividad al crear o modificar páginas con listados de elementos. En el artículo Mantenimiento CRUD en Apache Tapestry comento como conseguir un CRUD completo usando el componente Grid entre otras cosas.

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.


Referencia:
Mantenimiento CRUD en Apache Tapestry
Artículos sobre Tapestry en Blog Bitix
Artículos sobre Tapestry en El blog de pico.dev

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

Picando Código

Cómo se pronuncia: Astérix

April 29, 2016 08:45 PM

Astérix el Galo es una serie de cómics franceses creada por René Goscinny y Albert Uderzo. Las historias se publican como álbumes y están disponibles en español entre otros tantos idiomas. Siguen las aventuras de una irreductible aldea gala que resiste ahora y siempre al invasor, los romanos, en el año 50 a.c.

Mis padres comenzaron a coleccionar los álbumes en casa cuando era chico, por lo que pasé gran parte de mi infancia leyendo a Astérix. Ya de grande continué la colección por mi cuenta y la sigo manteniendo en mi biblioteca.

Astérix

Hace poco compartiendo nuestra afición por esta serie con mi amigo Martín iniciamos la discusión de cómo se pronuncia el nombre del héroe galo y el de su mejor amigo Obélix. Para mí siempre había sido de pronunciación aguda, y no necesitaba un tilde. Para Martín, se pronunciaba “Ásterix”, siendo esdrújula. Y ahí empezamos la investigación.

Las agudas que llevan tilde son las que terminan en n, s, o vocal. Así que si fuera aguda, no sería necesario el tilde. Las esdrújulas siempre llevan tilde, pero como es un nombre, la A va en mayúscula. Y podía ser que debiera llevar tilde pero siguiera la costumbre de no usarlo en mayúsculas.

Fuimos a los álbumes para ver qué nos revelaban. El primero que agarramos llevaba todos los diálogos en mayúsculas y ningún tilde, por lo que no se pudo determinar como se acentúan los nombres propios:

Abraracurcix menciona a Astérix y Obélix - Astérix en Helvecia

Abraracurcix menciona a Astérix y Obélix – Astérix en Helvecia

Pero recordaba que en otros más nuevos sí había tildes:

Obélix menciona a Astérix - La Odisea de Astérix

Obélix menciona a Astérix – La Odisea de Astérix

Se ve que el tilde se pone en la e, por lo que sería una pronunciación de palabra grave. También comparamos como en los primeros álbumes el nombre venía escrito sin tilde en la e y más adelante sí se empezó a agregar el tilde:

Logo Astérix

Llegamos a que Astérix es el nombre oficial en francés, por lo que se escribe así, pero no necesariamente se pronuncia con acento en la e. Escribiendo en un conversor texto-voz en francés los nombres “Astérix” y “Obélix”, escuchamos que la pronunciación correcta sería con acento en la última sílaba. Mirando unos videoclips de las películas, la pronunciación (tanto en español como en francés) también se presentaba así.

No contentos con el resultado, Martín siguió investigando hasta encontrarse con esta página de Wikipedia que le dió cierre al tema:

Los nombres de los personajes de la serie Astérix el Galo han ido variando con el tiempo. En primer lugar se han puesto los nombres dados en el sitio oficial, que en el caso de los dos personajes principales se escriben en francés: Astérix y Obélix. La pronunciación en cambio es aguda: Asteríx, Obelíx, aunque en algunas partes de Hispanoamérica se pronuncia como esdrújula o llana.

A modo de conclusión podemos definir que la pronunciación correcta es con acento en la última sílaba, a pesar de que lleven tilde en la e en francés. Pero de todas formas hay partes de Hispanoamérica donde se pronuncia de las otras dos formas posibles.

¿Tuviste el gusto de leer las historietas de Astérix? Nos intriga saber cómo pronunciabas su nombre:

Note: There is a poll embedded within this post, please visit the site to participate in this post's poll.

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

Poesía Binaria

Procesar argumentos de entrada en nuestros shell scripts con getopt

April 27, 2016 04:12 PM

mixer_4

Hace unos días analizamos cómo tratar los argumentos de entrada desde un shell script en Bash de una manera sencilla. Aunque, cuando la cosa se complica, debemos utilizar herramientas algo más avanzadas. Tal y como hicimos con getopt para C [parte 1, parte 2], vamos a hacer lo mismo en un shell script.

Aunque aquí tenemos dos posibilidades, que hacen prácticamente lo mismo getopt y getopts. Vamos a verlas detenidamente.

El programa de ejemplo

En este ejemplo, vamos a imaginar que el script hace una copia de seguridad. Dicha copia de seguridad, tendrá varios argumentos de entrada:

  • -v : Escribe en pantalla todo lo que está haciendo en cada momento
  • -l [archivo] : Escribe un log en el archivo
  • -z : Comprime la copia de seguridad
  • -c : Copia remotamente el backup a un servidor
  • -h : Servidor donde vamos a copiar el backup

Además, debemos decirle los directorios que vamos a copiar. Al menos uno será obligatorio. Además, si se especifica -c, será también obligatorio especificar -h.

getopts, terminado en s

Es el más sencillo, básicamente el uso de getopts lo haremos para no complicarnos la vida cuando un argumento tiene parámetro o no, ya que no tenemos que hacer shift como pasaba en la parte 1 de este tutorial.
Veamos un fragmento de código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash

VERBOSE=0
COPIAREMOTA=0

while getopts vzcl:h: option; do
  case $option in
    v)
      VERBOSE=1
      ;;
    z)
      COMPRIMIDO=1
      ;;
    c)
      COPIAREMOTA=1
      ;;
    l)
      LOGFILE=$OPTARG
      ;;
    h)
      HOSTCOPY=$OPTARG
      ;;
  esac
done

if [ $COPIAREMOTA -eq 1 ] && [ -z "$HOSTCOPY" ]
then
    echo "Si especifica copia remota -c DEBE especificar también host (-h)"
    exit 1
fi

shift $(( OPTIND - 1 ));
if [ $# -le 0 ]
then
    echo "Por favor, especifique los directorios a copiar"
    exit 1
fi

echo "VERBOSE: $VERBOSE"
echo "COMPRIMIDO: $COMPRIMIDO"
echo "LOGFILE: $LOGFILE"
echo "COPIA REMOTA: $COPIAREMOTA"
echo "HOSTCOPY: $HOSTCOPY"
echo "Directorios a copiar: $@"

El formato de utilización de getopts es:

getopts FORMATO VARIABLE [ARGS]

donde,

  • FORMATO será una cadena donde especificaremos los argumentos que serán flags, es decir, los que no necesitan nada más, y los que necesitan un segundo argumento para definir su funcionalidad. Estos últimos, vendrán acompañados de dos puntos (:).
  • VARIABLE será la variable de nuestro shell script donde almacenaremos el argumento que está analizándose ahora mismo
  • ARGS, si se especifica, indica de dónde se van a sacar los argumentos. Por defecto son los parámetros posicionales $1, $2, $3…

Lo bueno de utilizar getopts y no utilizar los métodos de la primera parte del tutorial es, además, que podemos combinar varios argumentos de entrada en un mismo argumento físico. Dicho de otra forma, no tenemos por qué llamar al programa así:

./test -v -z -c -h SERVIDOR directorio1, directorio2, …, directorioN

podemos hacerlo así:

./test vzch SERVIDOR directorio1, directorio2, …, directorioN

y esto lo hace mucho más amigable al usuario.

El gran problema de getopts, es que si los directorios a copiar los ponemos al principio, o incluso en medio, esos argumentos no se parsearán, y tal vez el comportamiento del programa no sea el deseado.

getopt, sin s, el más flexible

Pero claro, si queremos que nuestro programa acepte argumentos largos (–verbose para -v ; –zip para -z ; –copy para -c ; –log para -l ; –host para -h ) y, además, no queremos tener el problema de que el resto de argumentos se especifiquen en cualquier lugar (al principio, en medio o al final), la solución es getopt, sin s. Con esta utilidad, lo malo es que tenemos que andarnos con los shift de nuevo, pero yo creo que la flexibilidad que nos brinda compensa ese pequeño dolor.

Veamos un ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/bin/bash

ARGS=$(getopt -q -o "vzcl:h:" -l "verbose,zip,copy,log:,host:" -n "argumentos" -- "$@");

if [ $? -ne 0 ];
then
    echo "Ha habido un error al parsear los argumentos"
    exit 1
fi

eval set -- "$ARGS";

VERBOSE=0
COPIAREMOTA=0

while [ $# -gt 0 ]; do
  case "$1" in
    -v|--verbose)
      VERBOSE=1
      ;;
    -z|--zip)
      COMPRIMIDO=1
      ;;
    -c|--copy)
      COPIAREMOTA=1
      ;;
    -l|--log)
      LOGFILE="$2"
      shift;
      ;;
    -h|--host)
      HOSTCOPY="$2"
      shift;
      ;;
    --)
      shift;
      break;
      ;;
  esac
  shift
done

if [ $COPIAREMOTA -eq 1 ] && [ -z "$HOSTCOPY" ]
then
    echo "Si especifica copia remota -c DEBE especificar también host (-h)"
    exit 1
fi

if [ $# -le 0 ]
then
    echo "Por favor, especifique los directorios a copiar"
    exit 1
fi

echo "VERBOSE: $VERBOSE"
echo "COMPRIMIDO: $COMPRIMIDO"
echo "LOGFILE: $LOGFILE"
echo "COPIA REMOTA: $COPIAREMOTA"
echo "HOSTCOPY: $HOSTCOPY"
echo "Directorios a copiar: $@"

Viendo esto, getopt, en realidad lo que hace es reordenar los argumentos, es decir, los argumentos que estén juntos (por ejemplo -cvzl) los separa (quedando -c -v -z -l), para que los podamos analizar mejor. Además, los argumentos que no tengan ninguna clave primero, vamos los que quedan sueltos, los pone al final, después de un último argumento “–” (dos guiones, WordPress me pone un guión sólo y me da cosa quitarlo)

Entonces, podemos analizar los argumentos desde la salida de getopt, no sin antes pasar esa variable a los argumentos posicionales (con eval set — “$ARGS”). Cuando estemos analizando, es importante marcar un fin al while, como en el ejemplo, no encontrar más argumentos de entrada, aunque si encontramos “–” hacemos un break y salimos, pero nunca se sabe lo que puede pasar en las salidas, o si un IFS se nos va de madre…

O si preferimos, podemos hacerlo con un for sobre los mismos argumentos, y olvidarnos del case, aunque también tiene sus contras, si queremos extraer los archivos sueltos, debemos hacerlo de otra forma también:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/bin/bash

ARGX=($(getopt -q -o "vzcl:h:" -l "verbose,zip,copy,log:,host:" -n "argumentos" -- "$@"));

if [ $? -ne 0 ];
then
    echo "Ha habido un error al parsear los argumentos"
    exit 1
fi

VERBOSE=0
COPIAREMOTA=0

for (( arg=0; $arg<$# ; arg++ ))
do
  case "${ARGX[$arg]}" in
    -v|--verbose)
      VERBOSE=1
      ;;
    -z|--zip)
      COMPRIMIDO=1
      ;;
    -c|--copy)
      COPIAREMOTA=1
      ;;
    -l|--log)
      ((arg++))
      LOGFILE="${ARGX[$arg]}"
      ;;
    -h|--host)
      ((arg++))
      HOSTCOPY="${ARGX[$arg]}"
      ;;
    --)
      ULTIMO=$arg+1;
      break;
      ;;
  esac
done

if [ $COPIAREMOTA -eq 1 ] && [ -z "$HOSTCOPY" ]
then
    echo "Si especifica copia remota -c DEBE especificar también host (-h)"
    exit 1
fi

if [ $# -le 0 ]
then
    echo "Por favor, especifique los directorios a copiar"
    exit 1
fi

echo "VERBOSE: $VERBOSE"
echo "COMPRIMIDO: $COMPRIMIDO"
echo "LOGFILE: $LOGFILE"
echo "COPIA REMOTA: $COPIAREMOTA"
echo "HOSTCOPY: $HOSTCOPY"
echo "Directorios a copiar: "
for ((dir=$ULTIMO; $dir<$#; dir++))
do
    echo ${ARGX[dir]}
done

Para los curiosos

Algo que podemos probar es que, estas herramientas no están limitadas a los argumentos de entrada al script. Pueden ser argumentos de entrada de una función de Bash, o incluso podremos especificar en una cadena de caracteres lo que queremos que getopt o getopts parsee.

Más info

Getopts tutorial
Command line options
Foto principal: freestocks.org

The post Procesar argumentos de entrada en nuestros shell scripts con getopt appeared first on Poesía Binaria.

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

Arragonán

Semanas 410 y 411

April 27, 2016 12:25 PM

Y ahí viene otro nuevo pack de 2 semanas, a lo tonto voy a tener que renombrar la categoría a retro quincenal :P.

La primera semana de estas, se me juntaron muchas cosas en un par de días: cierre del trimestre, reuniones de varias horas, tener que terminar cosas para una demo y un lanzamiento a producción. Vamos, que tocaron unas cuantas noches de Beer Driven Development, una de las especialidades de la casa… lo malo que bajo presión apetece bastante menos.

El resto de la semana ya pude ir a un ritmo más sostenible y dedicarme a temas relacionados con la organización de la 3ª edición del Startup Open Space. A nivel organizativo creo que se nota que llevamos cierto rodaje y fue bastante fino: se repartió mejor el trabajo entre los organizadores y nadie se quemó al nivel de los años anteriores, tuvimos fruta y pastas a cascoporro todo el día, el catering estuvo bastante bien… y respondimos rápido a un problemón con las salas de etopia que íbamos a utilizar, tuvimos que improvisar y los asistentes casi ni se percataron (aunque algunos organizadores nos quedáramos sin la primera sesión por reorganizar todo). A nivel de contenidos creo que estuvo bastante bien, pero es un Open Space, así que eso es responsabilidad de las 100 personas que asistimos.

Supongo que tarde o temprano aparecerán más fotos, por el momento hay un álbum de Jesús Cuadra, otros andaban por ahí echando fotos. Además de los amigos de ¿Qué pasa co! que también iban grabando, a ver si sacan algo para su videoblog.

Al siguiente fin de semana me pasé por el hackathon Space Apps, al no poder estar mucho tiempo sólo pude echar una mano muy por encima a un par de equipos. Pude ver el buen ambiente que había y me volvió a picar el gusanillo de asistir a alguno en calidad de participante, que llevo ya bastante tiempo sin hacerlo.

Y en cuanto a proyectos en marcha:

  • Estuve dando soporte en algunas cosas al proyecto con Maubic, implementando algunos cambios en un par de microservicios.
  • Tras una semana de re-organización, nos pusimos a tope con el proyecto que nos han contratado a Coding Stones. Poco a poco se va notando que estamos engrasando la maquinaria, aunque esperamos que tal y como van pasando las semanas seguimos mejorando como equipo y mientras seguimos avanzando con el proyecto. Seguramente unos y otros vayamos escribiendo sobre lo que estamos haciendo.
  • Lanzamos la nueva web de Outreach Tool. También tocó implementar algunos cambios a nivel de programación para soportar algunas necesidades que fueron surgiendo. Todavía quedan bastantes mejoras en el tintero que vamos a ir haciendo poco a poco.

Buena semana.

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

Variable not found

Confluencia de ramas en el pipeline de ASP.NET Core

April 27, 2016 07:54 AM

ASP.NET Core
Al hilo de lo tratado en el último post sobre la ramificación del pipeline de ASP.NET Core utilizando los extensores Map() y MapWhen(), el amigo Fernando V. dejaba una pregunta interesante en los comentarios del blog:
"[…] Igual que hemos visto cómo ramificar el pipeline, sería posible volver a unir las distintas ramas en una sola?"
Tampoco creo que sea muy habitual crear este tipo de estructuras en el pipeline, pero la verdad es que me ha parecido un reto muy interesante, lo suficiente como para dedicarle un post ;D

Definición de objetivos

Pipeline con distintos recorridosEl objetivo es configurar el pipeline de forma que la ejecución pueda desviarse a una rama determinada, pero que al finalizar ésta vuelva a la rama principal.

Seguro que lo entendéis mejor si echáis un vistazo al diagrama lateral, donde representamos una estructura en la que:
  • Existe una rama principal del pipeline, en la que posicionamos un middleware llamado "Middleware 1".
  • A continuación introducimos una rama para las peticiones cuya ruta que comience por "/test", en la que introducimos un middleware llamado "Middleware 2.2".
  • Mientras en la rama principal, fuera de esta ramificación que hemos creado, introducimos el "Middleware 2.1".
  • Por último, ambas ramas confluyen en un punto común, en el que hemos posicionado el "Middleware 3".
De esta forma, el proceso de una petición a la ruta "/" sería procesada secuencialmente por los middlewares "1", "2.1" y "3", mientras que una petición a "/test" ejecutaría los middlewares "1", "2.2" y "3".

¿Interesante, eh? ;D

Tras reflexionar un poco, veo que el problema podría solucionarse al menos de dos formas, ambas igualmente divertidas ;D y que, en cualquier caso, ayudan a conocer mejor cómo funciona todo esto por dentro:
  1. Creando un middleware que permita configurar pipelines alternativos, similar a los extensores Map() o MapWhen() que hemos visto en otro post.
  2. Troceando el pipeline en varias secciones que engancharemos entre si a nuestra conveniencia.
Vamos a ver ambas implementaciones, pero antes permitidme un inciso…

Un middleware que deja rastro: TrackMiddleware

Vamos a echar un ojo rápidamente al middleware que vamos a utilizar para probar nuestras implementaciones, un componente que simplemente retornará al cliente la cadena que le indiquemos y continuará ejecutando el pipeline. Si esto de programar middlewares y lo que ves a continuación te suena a arameo antiguo, quizás deberías echar un vistazo al post Custom middlewares en ASP.NET Core que publicamos por aquí hace algún tiempo, donde explicaba cómo crear middlewares personalizados.

Bien, pues el código del middleware es el siguiente:
public class TrackMiddleware
{
private readonly RequestDelegate _next;
private readonly string _text;

public TrackMiddleware(RequestDelegate next, string text)
{
_next = next;
_text = text;
}

public async Task Invoke(HttpContext ctx)
{
await ctx.Response
                 .WriteAsync($"{_text} in path {ctx.Request.Path}\n");
await _next(ctx);
}
}
Como podéis observar, simplemente escribimos por el canal de salida el mensaje especificado, y pasamos el control al siguiente middleware. Así de sencillo.

Ahora veamos el clásico extensor para facilitar la inserción del middleware anterior en el pipeline:
public static class TrackMiddlewareExtensions
{
public static IApplicationBuilder UseTracking(this IApplicationBuilder app,
                                                  string text)
{
return app.UseMiddleware<TrackMiddleware>(text);
}
}
De esta forma, un ejemplo de uso podría ser:
public void Configure(IApplicationBuilder app)
{
app.UseTracking("Start of pipeline");
...
app.UseTracking("End of pipeline");
}
Pipelines alternativosY dicho todo esto, vamos al tema objeto del post…

Solución 1: Pipelines alternativos con IfMiddleware

La idea aquí consiste en implementar un middleware similar a MapWhen() que hemos visto en otros posts, pero que admita la configuración de dos pipelines, uno para cuando el predicado sea cierto y otro para cuando sea falso. Gráficamente, lo que vamos a montar es algo similar lo que vemos en el diagrama adjunto:
  • Tendremos el pipeline principal, en el que insertaremos el middleware "1".
  • A continuación, sobre el mismo pipeline principal, colocaremos el middleware IfMiddleware cuya codificación vamos a ver más adelante. En él configuraremos la condición que queremos evaluar, y los dos branches que ejecutarán las peticiones cuando dicha condición sea cierta o falsa, respectivamente.
  • En el branch que se ejecutará cuando la condición sea cierta, añadiremos el middleware "2.2". Para el caso contrario, insertaremos el middleware "2.1" en el otro branch.
  • Finalmente, de vuelta en el pipeline principal, insertaremos el middleware "3".
Comenzando por el final, a nivel de código lo configuraremos de la siguiente forma en la clase de inicialización de la aplicación:
public void Configure(IApplicationBuilder app)
{
app.UseTracking("Middleware 1");
app.UseIf(
ctx => ctx.Request.Path.StartsWithSegments("/test"),
trueBranch =>
{
trueBranch.UseTracking("Middleware 2.2");
},
falseBranch =>
{
falseBranch.UseTracking("Middleware 2.1");
});
app.UseTracking("Middleware 3");
}
Como podemos observar, en el primer parámetro de UseIf() indicamos la condición que queremos que se cumpla; a continuación, el delegado que configura el pipeline para cuando la condición evalúe a cierto, y seguidamente el delegado para cuando la condición sea falsa.

Así, una petición hacia el raíz retornará los mensajes emitidos por los middlewares "1", "2.1" y "3", mientras que si la ruta de la petición fuera "/test" se retornaría "1", "2.2" y "3".

El código del middleware sería el mostrado a continuación (ojo, he omitido comprobaciones de nulos o valores inválidos en parámetros para abreviar):
public class IfMiddleware
{
private readonly RequestDelegate _next;
private readonly IfOptions _options;

public IfMiddleware(RequestDelegate next, IfOptions options)
{
_next = next;
_options = options;
}

public async Task Invoke(HttpContext context)
{
if (this._options.Predicate(context)) {
await this._options.TrueBranch(context);
}
else {
await this._options.FalseBranch(context);
}
await this._next(context);
}
}
Y por último, veamos cómo implementar el extensor UseIf() que hemos utilizado más arriba para incluir IfMiddleware en el pipeline y configurarlo apropiadamente:
public static class IfExtensions
{
public static IApplicationBuilder UseIf(
this IApplicationBuilder app,
Func<HttpContext, bool> predicate,
Action<IApplicationBuilder> trueConfig,
Action<IApplicationBuilder> falseConfig)
{
// Create and configure the "true" branch
IApplicationBuilder trueAppBuilder = app.New();
trueConfig(trueAppBuilder);

// Create and configure the "false" branch
IApplicationBuilder falseAppBuilder = app.New();
falseConfig(falseAppBuilder);

// Add middleware to the pipeline
var options = new IfOptions()
{
Predicate = predicate,
TrueBranch = trueAppBuilder.Build(),
FalseBranch = falseAppBuilder.Build()
};
return app.UseMiddleware<IfMiddleware>(options);
}
}
Con esto habríamos conseguido nuestro objetivo. Quizás parezca algo aparatoso, pero hemos implementado una solución genérica para crear ramas alternativas en el pipeline, que quizás en algún momento pueda resultarnos útil.

Pipeline por tramosSolución 2: troceando el pipeline

Con la primera opción que hemos visto ya podríamos solucionar el problema que nos planteábamos en un escenario real, pero vamos a ver esta segunda opción para seguir aprendiendo sobre el funcionamiento del pipeline de ASP.NET Core :)

La idea en esta ocasión será "trocear" el pipeline de la aplicación en distintas porciones, y empalmarlas en los puntos que nos interese para conseguir nuestro objetivo. En el diagrama adjunto podéis ver que tendremos tres pipelines distintos:
  1. El pipeline principal, donde posicionaremos el middleware "1" y el "2.1".
  2. El pipeline de la rama que se activará sólo cuando se realicen peticiones a "/test", donde introduciremos el middleware "2.2".
  3. El pipeline común, en el que insertaremos el middleware "3" y a donde debe llegar la ejecución independientemente del camino tomado.
En este caso, dado que no necesitamos middlewares ni otros componentes que no vengan de serie en el framework, vamos a implementación la solución completa desde el método Configure() de la clase de inicialización:
public void Configure(IApplicationBuilder app)
{
// Create and configure the common pipeline (the last segment)
var common = app.New();
common.UseTracking("Middleware 3");
var commonPipeline = common.Build();

// Configure the main (default) pipeline
app.UseTracking("Middleware 1");
app.Map("/test", branch =>
{
// Configure the branch
branch.UseTracking("Middleware 2.2");
branch.Run(commonPipeline); // Join the "common" pipeline
});

// Continue configuration of main pipeline
app.UseTracking("Middleware 2.1");
app.Run(commonPipeline); // Join the "common" pipeline
}
Lo que hacemos es lo siguiente:
  • En primer lugar creamos y configuramos el último tramo del pipeline (azul en el diagrama), al que llamamos commonPipeline.
  • A continuación configuramos el pipeline principal (en verde):
    • Introducimos el middleware "1",
    • Seguidamente añadimos MapMiddleware para crear el branch condicional (en rojo):
      • le añadimos el middleware "2.2"…
      • y lo "enganchamos" con commonPipeline.
    • Volviendo al pipeline principal, le añadimos ahora el middleware "2.1".
    • Lo "enganchamos" a commonPipeline.
Observad que cuando hablamos de "enganchar" un pipeline con otro, en la práctica no es más que hacer que el primer pipeline acabe mediante una invocación al segundo, utilizando una sobrecarga del extensor Run() de IApplicationBuilder.

¡Y esto, es todo! De nuevo tendríamos solucionado el problema que proponíamos al principio del post con una solución más sencilla de implementar, aunque menos potente y elegante que la primera que vimos.

Como decía anteriormente, seguro que lo que hemos visto no nos va a ser de utilidad todos los días, pero está bien saber que existen estas posibilidades y, en cualquier caso, ha sido un ejercicio interesante para comprender mejor cómo funciona el pipeline y conocer la flexibilidad de su diseño.

Y, por supuesto, agradecer a Fernando y muchos otros que, como él, siguen haciendo de este espacio un lugar donde aprender juntos.

Publicado en Variable not found.

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

Jesús Perales

org.hibernate.HibernateException: Wrong column type Found

April 25, 2016 05:26 PM

org.hibernate.HibernateException:  Wrong column type Found

Algunas veces cuando intentamos relacionar las columnas de la base de datos a Hibernate necesitamos valores booleanos.

En la mayoría de lo los motores de base de datos que conozco no existen, así que optamos por utilizar valores enteros como tinyint, smallint o bit para representarlos.

Al intentar crear esta relación a una columna tinyint o similar puede que Hibernate lance una excepción diciendo que el tipo de dato es incorrecto.

Wrong column type. Found: tinyint, expected: smallint

Para solucionar esto es necesario agregar una propiedad en la anotación @Column con el nombre columnDefinition y en ella pasar el tipo de dato de la columna de nuestra tabla.

Ejemplo:

@Column(name = "column_name", columnDefinition = "TINYINT")

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

Poesía Binaria

Hosting compartido adaptado a los tiempos actuales, moderno y seguro

April 25, 2016 07:50 AM

photo-1453230806017-56d81464b6c5

Desde que empecé con Internet a mediados de la década de los 90 y mi proveedor de Internet me regaló 5Mb de hospedaje para alojar mi web siempre me ha gustado tener un rinconcito de Internet que fuera mío, para expresarme y compartir mi visión del mundo (me veo ya un poco viejete). Por aquel entonces, el hospedaje, era sólo de archivos HTML con algunas imágenes.

Con el paso del tiempo, terminé contratando mi dominio y un hospedaje mejor, con soporte de bases de datos, lenguajes de servidor y muchas cosas más, para hacer tanto mi experiencia como webmaster como la experiencia de mis usuarios un poco mejor. Aunque en todos estos años me he encontrado de todo, y es normal, ya prácticamente he visto evolucionar el mercado del hospedaje web.

¿Mucha lectura? ¡Ve al grano!

Este post contiene mucho texto y batallitas, pero si has venido aquí para que te recomiende un servicio de hosting moderno, seguro y con la última tecnología, pincha aquí y sáltatelo todo.
Pero no te quito de leerte todo esto.

Un poco de historia

Al principio este hospedaje compartido era muy sencillo. Con un poco de idea de Linux (Sistema que prácticamente había aprendido a leer) ya valía, apenas se trabajaba en seguridad. Era algo costoso, reducía recursos (que eran caros) y claro, es un negocio, cuantos más usuarios alojara por servidor, más dinero cobraba. Por otro lado, los ataques a sistemas no eran tan tan comunes como ahora. (Yo mismo gestiono algún servidor que recibe varios cientos de intentos de ataque diarios).

A medida que todo va evolucionando, surge el más temido dilema para un proveedor de servidor. ¿Le cortamos las alas al usuario o dejamos un poco de lado la seguridad? El problema es que si le cortas las alas al usuario nadie va a contratar tus servicios, y eso es un riesgo, la mayoría de empresas ponían en sus condiciones de servicio que si eres cliente, no les hackearas, ni intentaras acceder a cuentas de otros clientes. Así que en la década del 2000 al 2010, podíamos hacer cosas muy curiosas.

Había empresas de hosting (espero que ya no queden de esas), en las que, una vez que eras cliente, podías obtener un listado de todos los clientes que había alojados en tu misma máquina, e incluso con un poco de paciencia obtener direcciones de correo, mensajes, descargar webs enteras, bases de datos contraseñas de usuarios ¡en algunos casos en texto plano! y en otros sus hashes. Y claro, si eras de esos 200 usuarios que estaban en una máquina y podías ver aquello, no te apetecía que alguien no tuviera buenas intenciones y traficara con tus datos.
Poco a poco fue mejorando la situación, y se fue evolucionando en este aspecto, se fue utilizando mejor el sistema de privilegios de usuarios, y claro, ahora, en este tipo de hospedaje no teníamos sólo usuario www-data o httpd, sino que cada usuario tenía un usuario y el servidor web accedía con ese usuario para leer y escribir los archivos. Por lo que, aparentemente necesitas saber los credenciales del usuario para poder acceder a sus archivos.

Era de vulnerabilidades y recursos de sistema

Llegados a este punto, en el que los servidores compartidos tenían los usuarios del sistema delimitados, y un montón de nombres de usuario y contraseñas únicos para cada cliente del sistema haciendo que todos los archivos no sean accesibles fácilmente, bueno, siempre estaban los propios usuarios que ponían permisos 777 (lectura, escritura y ejecución permitidos para todos los usuarios del sistema), pero ya era problema de esos usuarios (en principio). Ahora llega el turno a las vulnerabilidades en los servidores (en realidad este turno nunca se ha ido).

6883638372_875237b271_k
Internet es más rápido, y el número de personas que intentan explotar vulnerabilidades también y, desde fuera se ha podido incluso ejecutar código con privilegios en un servidor en el que ni siquiera eres usuario, ya sea por configuración de seguridad inexistente o poco segura, o por aprovechar una vulnerabilidad del sistema. En realidad todos los proveedores deberían actualizar a menudo sus sistemas, aunque en la práctica no es tan fácil, tienes que tenerlo todo muy en orden para poder actualizar las actualizaciones sin que sea demasiado doloroso y sólo unos pocos lo consiguen

Por ejemplo, yo me he encontrado también con proveedores de hosting en los que se juntaba todo. Al ser accesibles desde el usuario perteneciente al servidor web todos los datos de los usuarios, se aprovecharon vulnerabilidades de Drupal y de Apache para que todos los usuarios de un mismo hosting compartido fueran afectados por un virus hecho en PHP. Se aprovechaban de todos los archivos .php que tenían permiso de escritura y eran accesibles por él (no tienen por qué tener permisos globales, sólo permiso de escritura en el propio servidor, cosa que a veces venía por defecto) para introducir un fragmento de código que se auto-replicaba y arruinar las webs de todos los usuarios de un hosting compartido (desde cambiar por completo tu web, hasta hacer suplantaciones para phising, envío de correos no deseados, introducir iframes, enviar a tus visitantes javascript maliciosos o incluso virus, normalmente para Windows.

Junto con el problema anterior, nos encontramos con que los sistemas van siendo más rápidos, y capaces de hacer muchas cosas. Lenguajes como PHP han evolucionado mucho y dejan hacer al usuario muchas cosas. También hay más usuarios en Internet, muchos empiezan a utilizar sus móviles para entrar en servicios web y eso son muchas más visitas. Y, un servidor, no por tener una visita desde un móvil va a gastar menos recursos en servirla. Todo esto hace que un servidor de hosting compartido pueda otorgar a un mismo usuario más recursos que al resto, según lo vayan requiriendo los usuarios. Sólo hemos dividido el ordenador por usuarios, pero asignar unos recursos específicos a un usuario ya es otra cosa, ahí tendríamos un problema de software y de hardware a la vez. Y es que, si un usuario no requiere la CPU y otro sí, no pasa nada. Lo verdaderamente malo es cuando un usuario está consumiendo sus recursos y los de los usuarios de su máquina y de repente un vecino necesita unos pocos recursos para atender una petición, y que o vaya muy lenta o no pueda. Es como si en un bloque de pisos un sólo vecino acaparara el ascensor durante todo el día, tarde o temprano la comunidad se queja.

¿Virtualización?

Llegados a este punto, la solución de un servidor privado virtual (VPS) parece más o menos lógica. Aunque esto sería una modalidad más grande y avanzada. Crearíamos un compartimento en nuestro servidor sólo para el usuario, para que se instale lo que el usuario quiera, eso está muy bien. Pero, antes se podían alojar muchísimos usuarios por máquina, creando máquinas virtuales dentro de un servidor podríamos alojar a muchos menos clientes, cada uno tendría un trozo de hardware, estaría ejecutando su Linux, su servidor web y sus servicios de sistema por separado. Por un lado sería una ejecución redundante dentro de una máquina, aunque, eso sí, cada uno tiene sus recursos y no se puede salir de ahí, además, tendrá su dirección IP para acceder a los servicios, y se pueden hacer cosas muy chulas con los VPS, puedes alojar los servicios críticos en máquinas detrás de una DMZ y, por ejemplo, ante una vulnerabilidad del sistema operativo o un cuelgue, la afectada es la máquina virtual, y ante un usuario que consuma muchos recursos, dicho usuario será el único perjudicado que sufrirá una pérdida sustancial de rendimiento sin afectar a sus vecinos. Eso sí, generalmente esto es mucho más caro que un hosting compartido y normalmente con mucha más capacidad. Aunque, por ahí van los tiros.

Linux Containers (LXC)

Pronto se empezaron a utilizar tecnologías como chroot o las FreeBSD jails a modo de “enjaular procesos” para que no pudieran salirse de unos límites. Aunque en 2008-2009, empezó una tecnología llamada LXC que, básicamente, aísla recursos de sistema junto con un espacio de procesos, utilizando tecnologías de virtualización. Ahora no es necesario emular un hardware para cada usuario, ni que los usuarios instalen sistemas operativos completos para cada uno. Ahora podemos crear un espacio propio para cada usuario dentro de una máquina principal imposibilitando que un usuario le quite los recursos a otro y que un usuario entre en el espacio de otro tanto en memoria como en disco. Esta implantación se hace a nivel de kernel acompañado de algunas aplicaciones que permiten, desde el propio espacio de usuario crear, destruir y administrar contenedores. Es más, podemos, en tiempo real, sin necesidad de reiniciar nada, aumentar las capacidades de un usuario siempre que en la máquina principal haya recursos.

Si habéis oído hablar de docker. Este último utiliza las capacidades de LXC.

Siteground, un hosting moderno y robusto

Siteground fue uno de los primeros proveedores de hosting en incorporar Linux Containers en su hosting Cloud, cuando esta tecnología ha estado lo suficientemente madura; y, desde finales del año pasado, todos sus hospedajes compartidos incorporan esta nueva tecnología. De esta forma, tu hospedaje es totalmente seguro y queda aislado completamente del resto de usuarios. Asimismo, otro usuario no podrá invadir tus recursos, ni tú tampoco a los demás usuarios y, por culpa de la explotación de una vulnerabilidad en la cuenta de otro usuario, tu web no se verá afectada. Sería como si te dieran una máquina únicamente para ti. Pero en realidad es un servicio de hospedaje compartido. Y, por ello, barato.

Web Hosting
Nota: Si utilizáis este banner para contratar, estaréis colaborando con este blog y os estaré muy agradecido.

Por si fuera poco, en todos sus hospedajes compartidos incorporan discos SSD por lo que la velocidad de E/S no será un cuello de botella, podrás utilizar bases de datos, lectura y escritura intensiva de archivos sin ningún problema ni pérdidas de velocidad.

Los que me conocéis, sabéis que a mi me gusta cocinarme los servidores desde el principio, instalar aplicaciones, configurar un firewall, etc. Aunque para ciertos servicios o webs viene muy bien tener un hosting ya configurado y listo para ser usado fácilmente. Además con una serie de extras: podemos utilizar PHP7 y HHVM en nuestras webs o incluso elegir el centro de datos donde serán hospedadas las páginas (cuanto más cerca, mejor experiencia para nuestros usuarios de esa zona), incluso podemos gestionar varios dominios con la misma cuenta siempre que entre todas no consuman todos los recursos de cada plan.

Sobre este post

Este es un post patrocinado por Siteground España. Hasta el momento, es el primer post patrocinado que he accedido a hacer a pesar de que varias empresas me lo han pedido y he terminado siempre rechazando porque el producto no me convencía. Este no ha sido el caso.

Foto: Vladimir Kudinov
Foto francotirador: Andre Gustavo Stumpf

The post Hosting compartido adaptado a los tiempos actuales, moderno y seguro appeared first on Poesía Binaria.

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

Variable not found

Enlaces interesantes 238

April 25, 2016 07:00 AM

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

.Net

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

Blog Bitix

Tutorial sobre los tipos genéricos de Java

April 24, 2016 11:00 AM

Hace ya más de una década que en Java 5 se introdujeron los generics para dotar al lenguaje de una mejor comprobación de tipos en tiempo de compilación y al mismo tiempo eliminar los cast que hasta entonces eran necesarios al usar las colecciones. Dada la lentitud de adopción que suele haber en la plataforma Java en los grandes entornos empresariales puede que aún no los hayamos usado extensamente o tengamos alguna duda en su uso. Hay unos cuantos conceptos sobre los generics que son convenientes conocer.

Java

Los generics fueron introducidos en la versión 5 de Java en 2004 junto con otras muchas novedades suponiendo en su historia una de las mayores modificaciones o al mismo nivel de las novedades introducidas con Java 8 más recientemente al lenguaje Java. Los generics son importantes ya que permiten al compilador informar de muchos errores de compilación que hasta el momento solo se descubrirían en tiempo de ejecución, al mismo tiempo permiten eliminar los cast simplificando, reduciendo la repetición y aumentando la legibilidad el código. Los errores por cast inválido son especialmente problemáticos de debuggear ya que el error se suele producir en un sitio alejado del de la causa.

Los generics permiten usar tipos para parametrizar las clases, interfaces y métodos al definirlas. Los beneficios son:

  • Comprobación de tipos más fuerte en tiempo de compilación.
  • Eliminación de casts aumentando la legibilidad del código.
  • Posibilidad de implementar algoritmos genéricos, con tipado seguro.

Un tipo usando generics tiene el siguiente aspecto, por ejemplo usando una clase Box contenedor de una referencia a un tipo no determinado en la definición de la clase pero que lo será en su uso. Una clase genérica puede tener múltiples argumentos de tipos y los argumentos pueden ser a su vez tipos genéricos. Después del nombre de la clase se puede indicar la lista de parámetros de tipos con el formato <T1, T2, T3, …>.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/Box.java">Box.java</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/Pair.java">Pair.java</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/OrderedPair.java">OrderedPair.java</pre></a></noscript>

Según las convenciones los nombres de los parámetros de tipo usados comúnmente son los siguientes:

  • E: elemento de una colección.
  • K: clave.
  • N: número.
  • T: tipo.
  • V: valor.
  • S, U, V etc: para segundos, terceros y cuartos tipos.

En el momento de la instanciación de un tipo genérico indicaremos el argumento para el tipo, en este caso Box contendrá una referencia a un tipo Integer. Con Java 7 se puede usar el operador diamond y el compilador inferirá el tipo según su definición para mayor claridad en el código. Podemos usar cualquiera de esta dos maneras prefiriendo usar el operador diamond por ser más clara.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/Instantation.java">Instantation.java</pre></a></noscript>

Para mantener la compatibilidad con versiones anteriores a Java 5 los tipos genéricos que al usarse no indican argumentos de tipo se denominan raw. El compilador indicará una advertencia como un uso potencialmente peligroso ya que no podrá validar los tipos.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/Raw.java">Raw.java</pre></a></noscript>

Además de las clases los métodos también pueden tener su propia definición de tipos genéricos.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/Method.java">Method.java</pre></a></noscript>

La sintaxis completa de uso sería:

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/MethodUsage.java">MethodUsage.java</pre></a></noscript>

Aunque puede abreviarse ya que el compilador puede inferir los tipos:

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/MethodUsageInference.java">MethodUsageInference.java</pre></a></noscript>

A veces querremos limitar los tipos que pueden ser usados empleando lo que se denomina bounded type. Con <U extends Number> el tipo U debe extender la clase Number.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/BoxBounds.java">BoxBounds.java</pre></a></noscript>

Una clase puede tener múltiples limitaciones, si una es una clase debe ser la primera y el resto de argumentos interfaces.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/Bounds.java">Bounds.java</pre></a></noscript>

En Java un tipo puede ser asignado a otro mientras el primero sea compatible con el segundo, es decir tengan una «relación es un». Una referencia de Object puede referenciar una instancia de Integer (un Integer es un Object).

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/IsA.java">IsA.java</pre></a></noscript>

Sin embargo, en el caso de los generics, ¿una referencia de Box<Number> puede aceptar una instancia Box<Integer> or Box<Double> aun siendo Integer y Double subtipos de Number?. La respuesta es no, ya que Box<Integer> y Box<Double> en Java no son subtipos de Box<Number>. La jerarquía de tipos es la siguiente:

Los tipos genéricos pueden extenderse o implementarse y mientras no se cambie el tipo del argumento la «relación es un» se preserva. De modo que ArrayList<String> es un subtipo de List<String> que a su vez es un subtipo de Collection<String>.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/PayloadList.java">PayloadList.java</pre></a></noscript>

En los generics un parámetro para un tipo ? se denomina wildcard siendo este un tipo desconocido. Son usados para reducir las restricciones de un tipo de modo que un método pueda funcionar con una lista de List<Integer>, List<Double> y List<Number>. El término List<Number> es más restrictivo que List<? extends Number> porque el primero solo acepta una lista de Number y el segundo una lista de Number o de sus subtipos. List<? extends Number> es un upper bounded wildcard.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/BoundedWildcard.java">BoundedWildcard.java</pre></a></noscript>

Se puede definir una lista de un tipo desconocido, List<?>, en casos en los que:

  • La funcionalidad se puede implementar usando un tipo Object.
  • Cuando el código usa métodos que no dependen del tipo de parámetro. Por ejemplo, List.size o List.clear.

Digamos que queremos definir un método que inserte objetos Integer en un List. Para mayor flexibilidad queremos que ese método pueda trabajar con cualquier tipo de lista que permita contener Integer, ya sea List<Integer>, List<Number> y List<Object>. Lo podemos conseguir definiendo List<? super Integer> que se conoce como Lower Bounded Wildcard.

Las clases genéricas no tienen relación alguna aunque sus tipos los tengan, pero usando wildcads podemos crearlas.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/WildcardList.java">WildcardList.java</pre></a></noscript>

Uno de las mayores confusiones al usar generics es cuando usar upper bounded wildcards o cuando usar lower bounded wildcards. Podemos usar las siguientes reglas:

  • Una variable generic que se usa como fuente de datos (in), por ejemplo src en <U> copy(List<? extends U> src, List<? super U> dest) se define usando upper bounded wildcard con la palabra clave extends. De modo que la lista del parámetro src pueda ser una lista de un tipo U o de un subtipo de U.
  • Un variable generic que se usa como destino de datos (out), por ejemplo dest en <U> copy(List<? extends U> src, List<? super U> dest) se define usando lower bounded wildcard con la palabra clave super. De modo que la lista del parámetro dest pueda ser una lista de un tipo U o de un supertipo de U.
  • En caso de que la variable pueda ser usando mediante métodos definidos en la clase Object se recomienda usar un unbounded wildcard (?).
  • En caso de que la variable se necesite usar como fuente de datos y como destino (in y out) no usar wildcard.

Los generics son un mecanismo para proporcionar comprobaciones en tiempo de compilación, sin embargo, el compilador aplica type erasure que implica:

  • Reemplazar todos los tipos con sus bounds o por Object si son unbounded.
  • Insertar casts para preservar el tipado seguro.
  • Generar métodos puente para preservar el polimorfismo en generics en los que son extendidos.

Un tipo non reifiable son aquellos cuya información de tipo ha sido eliminada en tiempo de compilación por el type erasure, para la JVM no hay ninguna diferencia en tiempo de ejecución entre List<String> y List<Number>. No se crean nuevas clases para los tipos parametrizados de modo que no hay ninguna penalización en tiempo de ejecución. Una clase genérica al compilarla se transforma aplicando type erasure:

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/TypeErasure.java">TypeErasure.java</pre></a></noscript>

Los generics tiene algunas restricciones:

  • No se pueden instanciar tipos genéricos con tipos primitivos.
  • No se pueden crear instancias de los parámetros de tipo.
  • No se pueden declarar campos static cuyos tipos son parámetros de tipo.
  • No se pueden usar casts o instanceof con tipos parametrizados.
  • No se pueden crear arrays de tipos parametrizados.
  • No se pueden crear, capturar o lanzar tipos parametrizados que extiendan de Throwable.
  • No se puede sobrecargar un método que tengan la misma firma que otro después del type erasure.

Este artículo es gran medida una traducción del tutorial de Java sobre Generics, que también es recomendable echarle un vistazo incluso leerlo varias veces por la cantidad de información que contiene, en algunos puntos todo lo comentado en este artículo está explicado de forma más extensa.

Para profundizar más en este importante tema de genéricos de Java tenemos a nuestra disposición varios libros, alguno como Java Generics and Collections dedicado en gran parte a él, no importa que se un libro del 2006 ya que desde entonces los genéricos no han tenido grandes cambios y su contenido sigue siendo válido. Los generics de Java no son perfectos, por el type erasure y ser non reifiables, pero tampoco débiles y hay buenos motivos para que sean así como se dice en el libro.

A pesar de los generics y el compilador es posible poner en un String en un HashSet<Integer> usando el tipo raw de HashSet, cosa que se denomina heap pollution y que provoca exepciones ClassCastException en tiempo de ejecución. Usando colecciones envueltas por los métodos Collections.checkedSet, checkedList y checkedMap evitaremos el heap pollution produciendo una excepción no en el momento de extraer el objeto de la colección sino en el momento de insertarlo.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/c5360a2d848a594c4dba518d6f1cfd62/raw/HeapPollution.java">HeapPollution.java</pre></a></noscript>

En resumen, los genéricos en Java son un añadido muy útil al lenguaje.

Referencia:
Novedades y nuevas características de Java 8
Introducción y nuevas características de Java EE 7
10 razones para seguir usando Java
Java Tutorials: Lesson: Generics
Java 1.5 Generics Tutorial

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

Poesía Binaria

Un tiempo de descanso obligado para el blog, historias y hexadecimales…

April 24, 2016 10:31 AM

malaga_beach

He estado unos días de vacaciones obligadas en lo que respecta al blog. Sin poder postear, sin poder programar mucho… de hecho, los que sigáis el blog, sólo habréis visto algunos posts programados desde hace tiempo, y no los he promocionado mucho por redes sociales… donde sólo he compartido alguna cosa pequeña.
El motivo de este parón repentino, como dije en el último BITes ha sido una lesión en la muñeca derecha que me provocaba muchos dolores al teclear. Al principio no parecía para tanto, pero luego estuve algo peor.
Pero bueno, después de perder un mes de vida informática (lo que equivale a algo más de 2 años humanos), toca ponerse al día en todas las tecnologías que han surgido y toda crear algunos posts para los lectores de este blog.

En estos días me he planteado muchas cosas, como hacerme uno de estos… no podría sustituir una mano completa, pero a lo mejor un par de pedales ayudan.
Otro tema sería el reconocimiento de voz en Linux. Aunque hay aplicaciones que funcionan realmente bien, al final están tirando de los servidores de reconocimiento de voz de Google y yo soy muy paranoico con eso, ese tipo de aplicaciones deberían ser 100% offline, sin depender de nadie… ¿y si el servidor se cae? He visto partidos de baloncesto retrasados por una actualización de Windows… no tiene mucho que ver, pero es que cuando tu software es dependiente de otro, pierdes el control sobre tu software.

Sin más dilación, voy a ponerme a postear un rato, que, por cierto hoy cumplo 20 años, y es que a partir de una edad, hay que empezar a decir los años que cumples en hexadecimal. Aunque también mola decir que tengo 100000 años :)

The post Un tiempo de descanso obligado para el blog, historias y hexadecimales… appeared first on Poesía Binaria.

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

Jesús Perales

Chromecast, lo bueno y lo malo

April 20, 2016 03:21 PM

Chromecast, lo bueno y lo malo

Hace ya tiempo que me hice de este aparato (Chromecast 1 por asi decirlo) y quería compartir mis opiniones y problemas sobre el mismo, a modo de reseña, pero en pocas palabras puedo decir que estoy mas que satisfecho con el, bonito, barato y bueno.

Lo bueno

El precio que pague por mi Chromecast fue de $35 dolares, un amigo iba ir a EUA y se lo encargue, en ese tiempo el cambio estaba al rededor de los $11 pesos mexicanos, así que podría decirse que me costo $385 pesos, una ganga en comparación del precio que tiene hoy en día en cualquier tienda de México, su precio es de casi $800 pesos, para mi fortuna mi amigo también se compro uno y como no le gusto me lo vendió en $200 pesos.

Es fácil de configurar, puede hacerse desde muchos dispositivos, personalmente lo configure desde celulares Android, IOS y PC's con Windows y con GNU/Linux pero tiene detalles.

Las aplicaciones son lo mejor que tiene, las que mas uso son Netflix, Youtube, Popcorntime y SoundCloud.

En estos días me dio por probar algunos juegos, como Just Dance Now que es muy divertido y hace que nos movamos un poco(en este juego sospecho que la calidad del celular perjudica mi puntuación).

También intente jugar Angry Birds Friends pero jamas apareció el botón para enviarlo al Chromecast como la app dice.

En Netflix las películas van bien, no tengo ningún problema relacionado con el Chromecast, quizá la única vez que me fallo fue por que saque a todos los dispositivos desde mi cuenta de Netflix y me vi en la necesidad de reiniciar el Chromecast, este servicio lo pague para poder ver Breaking Bad y lo sigo pagando para ver Better Call Saul.

Desde que compre mi Chromecast la televisión publica paso a ser Youtube, aveces llego a canales donde escribo el típico comentario de "¿Qué estoy haciendo con mi vida?", lo uso mas que nada para ver vídeos de música, programas infantiles(ahora ya existe Youtube Kids), noticieros(El pulso de la republica, Nopaltimes) y canales de entretenimiento(wtfqshow, Combo y DrossRotzank).

Generalmente para ver películas Netflix esta bien, pero aveces no encuentro las películas que quiero y recurro al inmortal Popcorntime, el control y el inicio de la película no es muy fluido, aveces al presionar el botón de Play tarda un rato en responder, la instalación no es muy intuitiva, en algunas ocasiones e tenido que cerrar y abrir el programa para que aparezca el botón para enviar a Chromecast, las películas siempre están en ingles y la mayoría de las veces tienen subtitulo, pero me gusta esta aplicación, sus pros sobrepasan de forma desproporcionada estos pequeños detalles.

SoundCloud lo utilizo relativamente poco, pero cumple con su principal funcionalidad y es la misma que encontramos en Spotify, seleccionas la pista y se reproduce en la televisión, creo que lo único que faltaría son animaciones al estilo Windows Media Player de la década pasada.

Lo malo

En un principio pude configurar sin problemas mi Chromecast en GNU/Linux(no recuerdo si Fedora o Ubuntu), pero después de un tiempo me apareció un mensaje en el que se decía que no era posible desde mi SO, tuve que configurar-lo desde un IOS.

Después note que se dejaban de escuchar los vídeos de Youtube, pasaba muy esporádica-mente pero tenia que apagar y volver a encender el televisor, aun no se si es algo de mi TV o si es un problema del Chromecast, pero como jamas me paso viendo un canal de la TV publica, supuse que es un problema del Chromecast, hace tiempo que no sucede, supongo que debió repararlo una actualización.

Muy pocas veces necesite tocar con mis manos el dispositivo mientras estaba encendido, pero me di cuenta que se calienta mucho y la temperatura es algo que me preocupa, pues en este tipo de dispositivos nunca es bueno el calor, entre mas frió mejor.

El cable auxiliar que viene en la caja es muy útil, funciona perfectamente para las entradas HDMI de difícil acceso, lo malo es que se descompuso después de que mi hijo lo usara como mordedera, no fueron mas de 10 minutos pero dejo de funcionar, es decir el cable es muy "delicado".

Quizá esto sea mas de un problema de una aplicación que de el propio Chromecast pero Google Photos para Android ya no envía las fotos que quiero, pareciera que le hubieran removido el soporte a dicha app y ahora solo puedo enviar fotos de Google Photos abriendo la aplicación de Chromecast e ir cambiando las fotos, eso no es nada intuitivo, no me quejaría de ello si no estuviera en la pagina oficial que Google Photos tiene soporte para Chromecast.

Si se quiere desarrollar algo compatible con este dispositivo, tienes que pagar, la suma no es grande pero desagrada, además la API y la documentación no me han gustado para nada y uno tiene que sentarse a dedicarle un par de horas para medio comprender lo que tratan de explicarnos.

Destripando el código de Popcortime me di cuenta que es muy sencillo enviar contenido local, sin tener que pagar, pero no encontré nada de ello en la documentación oficial, puede ser que no busque lo suficiente.

Conclusión

Recomiendo completamente el dispositivo, hace justamente lo que dice y en el momento que lo compre la relación precio-calidad era de lo mejor.

Puede que lo ponga en venta y me compre el Chromecast 2 que se supone es mas rápido y debería ser mejor, no tengo un punto de comparación con otros dispositivos como Roku nunca lo e utilizado, pero puedo decir que estoy mas que contento con este dispositivo.

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

Variable not found

Ramificar el pipeline de ASP.NET Core con Map() y MapWhen()

April 19, 2016 07:48 AM

ASP.NET Core pipeline
Al representar gráficamente el pipeline de ASP.NET Core, lo habitual es hacerlo como una tubería única en las que colocamos estratégicamente los middlewares que procesarán las peticiones que entrarán por ella.

Incluso a nivel de código, la inserción de middlewares se realiza normalmente invocando métodos del tipo UseXXX() sobre la instancia de IApplicationBuilder que recibimos en el método Configure() de la clase Startup de nuestras aplicaciones ASP.NET Core.

Esto parece dar a entender que el pipeline es lineal, y que todas las peticiones serán procesadas de la misma forma y por los mismos middlewares, salvo que alguno de ellos decida cortocircuitar la petición.

Ramas en el pipeline de ASP.NET CoreSin embargo, aunque eso será lo más frecuente, no es obligatoriamente así. Ya desde los tiempos de Katana tenemos la posibilidad de dividir el pipeline en branches o ramas en función de determinados criterios, permitiéndonos crear configuraciones mucho más complejas y potentes que las que habitualmente suelen mostrarse.

Como ejemplo, en el diagrama lateral podemos observar un pipeline en cuya entrada se encuentra un middleware de gestión de errores, y que a continuación se divide en función de la ruta de la petición de entrada. Así, las peticiones cuyas rutas comiencen por "/SignalR" serán procesadas por los middlewares configurados en la rama de la izquierda; las que comiencen por "/API" serán procesadas a lo largo de la rama derecha, y el resto continuará su proceso por la rama central.

Vamos a ver cómo conseguir crear estas estructuras, pero, como siempre, tened en cuenta que estamos aún trabajando con una versión preliminar de ASP.NET Core y hay detalles que podrían cambiar en la versión definitiva.

IAppBuilder.Map(): crear branches en función de la ruta

Map() es un extensor sobre IAppBuilder que permite crear una rama del pipeline a partir de una ruta determinada, en cuyo interior podremos configurar a su vez un nuevo IAppBuilder que definirá cómo van a procesarse las peticiones que circulen por su interior. Hmmm, dudo que haya quedado muy claro, así que mejor lo vemos con código:
public void Configure(IApplicationBuilder app)
{
...

app.Map("/API", (IApplicationBuilder branch) =>
{
branch.UseCors(…);
branch.UseLogging(…);
branch.UseNancy(…);
});

...
}
Como podemos observar, la llamada a Map() acepta un primer parámetro en el que indicamos que se trata de un mapeo que afectará a todas las peticiones que comiencen por "/API". Después, en el segundo parámetro, implementamos un delegado en el que configuramos esta rama del pipeline mediante las llamadas habituales a UseXXX(), pero esta vez sobre el IAppBuilder que nos envían como parámetro que representa a la rama recién creada.

Una vez dentro del delegado, todo el código de configuración que encontraremos es idéntico al que solemos encontrar en el método Configure(), pero sólo afectará a la nueva rama porque las llamadas las hacemos sobre el IApplicationBuilder de la rama.

Internamente, lo que hace la llamada a Map() es añadir al pipeline una instancia de MapMiddleware, un middleware que contiene un nuevo pipeline completo, que es el que configuramos desde el delegado anterior. Ya en ejecución, cuando llega una petición, el middleware comprueba si la ruta de entrada comienza por la que hayamos indicado y, en caso afirmativo, pasa el control a este pipeline en lugar de continuar la ejecución por la rama inicial.

¿Y no puedo usar otros criterios en lugar de sólo la ruta? Pues sí, pero en lugar de Map() tendremos que usar MapWhen().

IAppBuilder.MapWhen(): crear branches en función de cualquier condición

El extensor MapWhen() es básicamente igual que Map(), pero en lugar de indicar la ruta a partir de la cual se crea el nuevo pipeline, se puede especificar cualquier tipo de condición en forma de predicado. De hecho, podríamos considerar Map() como una particularización de MapWhen() porque podríamos escribirlo con relativa facilidad usando este último:
app.MapWhen(
ctx => ctx.Request.Path.StartsWithSegments("/API"),
branch =>
{
// Add middlewares for this branch here, for example:
// branch.UseCors();
}
);
El parámetro ctx que recibe el predicado es de tipo HttpContext, lo que nos da acceso a prácticamente cualquier dato de la petición o su contexto. El siguiente código muestra cómo usarlo para ramificar en función de la existencia de una cookie en la petición:
app.MapWhen(ctx => ctx.Request.Cookies.ContainsKey("auth"), 
branch =>
{
// Add middlewares to this branch here, for example:
// branch.UseIdentity();
}
);
Por último, sólo destacar que en caso de usar MapWhen(), debéis tener en cuenta que las condiciones del predicado se evaluarán en cada petición, por lo que implementar condiciones complejas o que requieran operaciones de entrada-salida podría penalizar seriamente el rendimiento de nuestras aplicaciones.

Publicado en Variable not found.

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

Poesía Binaria

Cómo procesar múltiples argumentos de entrada en scripts para Bash

April 18, 2016 08:22 AM

photo-1455540904194-fc101941273a_r

Los argumentos de entrada de un programa nos sirven para modificar ligeramente el comportamiento de un programa dentro de las opciones que nos permita su desarrollador. Y, sobre todo, siempre que trabajamos con programas para consola, es muy buena idea permitir que nuestros usuarios puedan utilizar funciones extras o especificar sus propios parámetros de trabajo sin necesidad de modificar el programa.

Si trabajáis con lenguajes como C o C++ podéis leer algo de información al respecto:

Pero, cuando hacemos algún script en Bash para línea de comandos, también puede resultar muy útil, tanto para especificar archivos de entrada, como las propias opciones de nuestro programa. Dicha gestión de argumentos la podemos hacer de varias maneras, dependiendo de lo avanzadas que sean nuestras necesidades.

Acceso básico a los argumentos de entrada

Como ocurre con las funciones, podemos acceder a los argumentos de entrada con $n siendo n el número del argumento que queremos pedir. Y no nos dará error si pedimos un argumento inexistente. Por lo que si hacemos este pequeño script:

1
2
3
4
5
6
7
#!/bin/bash

echo "Primero: $0"
echo "Segundo: $1"
echo "Tercero: $2"
echo "Cuarto:  $3"
echo "Quinto:  $4"

Y si lo ejecutamos de la siguiente forma, tendremos una salida parecida a esta:

$ bash argbash.sh escribo argumentos “para mi” programa
Primero: argbash.sh
Segundo: escribo
Tercero: argumentos
Cuarto: para mi
Quinto: programa

Nota: también podemos dar permisos de ejecución a argbash.sh y no hace falta llamar a bash primero.
Como siempre, en la línea de comandos, los argumentos vienen separados por espacios, pero si escribimos el argumento entre comillas, en ese sí que podremos escribir espacios.

Con esto y un poco manejo de condicionales, ya podremos tener un manejo básico de los argumentos de entrada de nuestro programa.

Contar argumentos del programa

1
2
3
#!/bin/bash

echo "Has pasado $# argumentos"

Así podemos saber cuántos argumentos nos han pasado, en el ejemplo anterior, devolvería 4. Hemos visto que existe $0, el cual indica el nombre de nuestro ejecutable (que también podemos jugar con él, hacer una condición dependiendo del nombre del ejecutable para correr un código u otro).
Pero en sí $# contará el número de argumentos propiamente dichos (no como en C, por ejemplo, en el que el argumento 0 sí que cuenta).

Comprobar argumento vacío

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

if [ -z "$3" ]
then
    echo "El tercer argumento está vacío"
fi

if [ -n "$2" ]
then
    echo "El segundo argumento tiene datos"
fi

Con estas líneas podemos comprobar si el tercer argumento existe o no (o al menos que esté vacío) y justo lo contrario con el segundo, es decir, ejecutaremos código cuando el segundo argumento tenga datos.

Iterando los argumentos

Pero vamos, lo interesante empieza ahora. Podemos hacer bucles que recorran los argumentos de entrada de nuestro programa, por ejemplo:

1
2
3
4
5
6
7
8
9
#!/bin/bash

CONTADOR=0

for i in "$@"
do
    let CONTADOR=$CONTADOR+1
    echo "Argumento $CONTADOR: $i"
done

La parte del contador no es estrictamente necesaria, pero da un toque de distinción. Desde aquí hemos recorrido todos los argumentos del programa, ya podremos procesarlos como queramos, o si son archivos de entrada, abrirlos, etc.

Pero también podemos hacerlo de otra forma, que nos puede resultar más cómoda en otras ocasiones, con un bucle while:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

CONTADOR=0

while [ "$*" ]
do
    let CONTADOR=$CONTADOR+1
    echo "Argumento $CONTADOR: $1"
    shift
done

De esta forma, dentro del while estamos preguntando: ¿hay argumentos? Si es así, aumentamos contador y mostramos el argumento 1. Luego llamamos a shift, que básicamente se carga el primer argumento desplazando todos los demás un lugar hacia la izquierda, es decir, el segundo ahora es el primero, el tercero ahora es el segundo y así sucesivamente.

Este tipo de bucles, nos ayudarán para el siguiente ejemplo, en el que puede ser que en alguna iteración debamos extraer más de un argumento y, aunque sería posible hacerlo con el for, complicaría un poco la lógica.

Parsear opciones de entrada

Una de los puntos fuertes es que podemos recorrer los argumentos de entrada de un programa para definir sus opciones. Por ejemplo, si nuestro programa acepta (es sólo un ejemplo, nuestro programa podrá aceptar cualquier argumento):

  • -h o –help : ayuda
  • -u o –user : introducir nombre de usuario
  • -s o –server : nombre del servidor
  • -v o –verbose : escribir en pantalla todos los mensajes

Podremos hacer un ejemplo como este:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/bin/bash

function showhelp()
{
    echo "Ayuda del programa"
    exit 0
}

# Mientras el número de argumentos NO SEA 0
while [ $# -ne 0 ]
do
    case "$1" in
    -h|--help)
        # No hacemos nada más, porque showhelp se saldrá del programa
        showhelp
        ;;
    -u|--user)
        USERNAME="$2"
        shift
        ;;
    -s|--server)
        SERVER="$2"
        shift
        ;;
    -v|--verbose)
        VERBOSE=1
        ;;
    *)
        echo "Argumento no válido"
        showhelp
        ;;
    esac
    shift
done

if [ -z "$USERNAME" ]
then
    echo "El nombre de usuario es OBLIGATORIO"
fi

if [ -z "$SERVER" ]
then
    SERVER="servidor.por.defecto.com"
fi

echo "Nombre de usuario: $USERNAME"
echo "Servidor: $SERVER"
if [ -n "$VERBOSE" ]
then
    echo "El modo VERBOSE está ACTIVADO"
else
    echo "El modo VERBOSE está DESACTIVADO"
fi

Como vemos, hemos utilizado un case para que, en cada iteración del while, analicemos el argumento actual y en consecuencia tomemos decisiones. En este caso, cuando el argumento es sencillo, como -v, le damos un valor a la variable VERBOSE y terminamos, cuando termina case, hay un shift, así que corremos los argumentos una posición y seguimos en el bucle. Pero cuando el argumento acepta un parámetro adicional, es decir “-u USUARIO”, debemos leer tanto “-u” como “USUARIO”, por eso en esa parte del case, asignamos el segundo argumento a la variable USERNAME y hacemos un shift, que acompañado al shift que hay detrás del case, hará que corramos los argumentos dos posiciones.

Para el siguiente post

Para no alargar mucho este post, he decidido cortar aquí para una segunda parte. Esa segunda parte tratará de cómo hacer esto mismo utilizando getopt, una utilidad que nos hará tener un uso un poco más avanzado de los argumentos de un programa y que, incluso puede tener utilidades muy interesantes.

Foto principal: freestocks.org

The post Cómo procesar múltiples argumentos de entrada en scripts para Bash appeared first on Poesía Binaria.

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

Variable not found

Enlaces interesantes 237

April 18, 2016 07:10 AM

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

.Net

ASP.NET

ASP.NET Core

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Otros


Publicado en Variable not found

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

Koalite

Ficheros y carpetas en un mundo de APIs Web

April 18, 2016 05:06 AM

Sería el año 2003 o 2004 cuando se celebró aquella reunión. En aquella época los servicios web, los de verdad, los de SOAP y WSDL, eran el no va más y yo todavía me creía lo que me contaban en las conferencias. Por tanto es normal que cuando alguien propuso utilizar ficheros para comunicar dos sistemas, yo le mirase con cara de “madre mía, de qué siglo has salido tú” y le explicara el atraso que eso suponía. Que lo que realmente necesitábamos para ese escenario eran servicios web, claro.

Al final el sistema se implementó con servicios web, funcionó y, de hecho, todavía sigue en producción y sin visos de ser reemplazado. Seguramente también habría funcionado dejando ficheros en carpetas, aunque la verdad es que eso no importa mucho ahora.

Hoy en día, los servicios web ya no están de moda, o al menos no los de SOAP y WSDL, pero a cambio la definición se ha relajado y abarca casi cualquier cosa con la que se pueda interactuar a través de HTTP. Hay APIs basadas en xml (aunque no SOAP), APIs más o menos RESTful, APIs estilo RPC… en fin, APIs para todos los gustos. A la hora integrar aplicaciones, siguen pareciendo la opción Buena™, a menos que necesites algo más exótico como una cola de mensajes o un bus, pero el 90% de las veces los puntos de integración que ofrecen los sistemas son APIs web.

Por eso tiene cierta gracia que, justamente ahora que los herederos de los servicios web que yo defendía hace tantos años dominan el mundo, me vea en la posición de mi interlocutor de aquella reunión y mi propuesta para integrar (ciertas) aplicaciones sea mediante ficheros de texto.

Porque sí, de eso va este post. De explicar por qué, en este mundo moderno y ultraconectado de APIs web, todavía hay escenarios en los que utilizar unos simples ficheros de texto para integrar aplicaciones puede ser una buena opción.

Empezamos.

welcome back to the nineties

Simplicidad al poder

En los más de diez años que han pasado entre aquella reunión y hoy, he cubierto mi cupo de integraciones con multitud de mecanismos diferentes. Toda la gama de APIs web, sockets con protocolos binarios, remoting, colas de mensajes, dlls, controles ActiveX, conexión serie, bases de datos…

¿Por qué entonces volver a utilizar ficheros de texto, como si esto fuese 1990?

La mayoría de las ventajas de utilizar ficheros de texto se derivan de lo extremadamente simple que es la solución.

Leer y escribir ficheros es algo fácil de realizar en prácticamente todos los lenguajes de programación, y si eliges un formato razonable (me da igual XML, JSON, YAML, CSV o lo que sea), no suele ser complicado encontrar librerías para tratarlos de una forma cómoda.

Al aislar el API de la capa de transporte, tienes mucha flexibilidad para trasportar esos ficheros. Desde una carpeta compartida en una red local, una conexión (s)ftp/scp o una carpeta sincronizada con DropBox, hasta un pendrive USB que puedas llevarte en el bolsillo (y, ojo, no subestimes el poder del pendrive que te puede salvar más de una vez). De hecho, siempre puedes implementar a posteriori una fachada sobre el sistema de integración que use HTTP de cara al exterior, pero genere ficheros hacia tu aplicación.

Utilizar algo tan básico como ficheros de texto hace que puedas aprovechar un montón de herramientas genéricas que ya dominas.

Puedes visualizarlos y modificarlos con tu editor de texto favorito. Es trivial hacer copias de ellos para ejecutar otra vez la integración en un entorno de depuración. También es fácil comparar el contenido después de distintas ejecuciones. Son estupendos para utilizar como salidas en tests de aprobación.

Todo esto se puede hacer con otros sistemas de integración, y actualmente existen herramientas como Fiddler o Postman muy potentes para ayudarnos a hacer cosas similares con APIs web, pero no dejan de ser más herramientas a manejar. Posiblemente a ti o a mi nos dé igual porque ya estamos acostumbrados a usarlas, pero créeme, para un usuario (y para ciertos “profesionales” de la informática) es mucho más sencillo copiarte los ficheros de una carpeta que capturarte una sesión con Fiddler.

Poder monitorizar el comportamiento de una integración basada en ficheros de texto es muy sencillo y nada intrusivo, incluso en producción. Basta con mirar lo que hay en una carpeta. Utilizando un poco de sentido común y un par de convenciones apropiadas, como mover/renombrar los ficheros procesados en función de si el proceso ha sido correcto o no, saber si el sistema está funcionando o hay errores es muy fácil. Poder automatizar “alertas” sobre eso, a partir de una tarea programada que haga un “dir *.error” y mande un email no cuesta nada. Claro que no tienes la sofisticación de un sistema de monitorización “de verdad”, pero es que, al igual que pasa con los logs, a veces no necesitas tanta complejidad.

Si la aplicación esta caída, el propio sistema de archivos actuará de almacenamiento intermedio, permitiendo que la información se siga recogiendo hasta que la aplicación vuelva a estar activa y pueda procesarlos. Incluso es factible copiar fácilmente todos los archivos a otra máquina y procesarlos en ella.

Si quieres alterar los datos de entrada para hacer pruebas, o incluso para corregir errores, sólo necesitas modificar los ficheros. Si un sistema recibe como entrada ficheros de texto y fallan porque algo está mal, podemos editarlos y volver a procesarlos, evitando perder la información. Hacer eso a través de un API web implica una coordinación mucho mayor con el cliente, o haber hecho un desarrollo específico para ello.

Algo similar ocurre si necesitamos “adaptar” los datos a la entrada o salida del sistema. En el caso de los ficheros de texto, meter entre los sistemas integrados un script o aplicación que haga una transformación mientras los mueve de carpeta es mucho más sencillo que meter un proxy delante de un API web para transformar las peticiones o las respuestas.

Hay ocasiones en que necesitamos interactuar con el sistema que genera la información, por ejemplo para forzar esa generación o para pasar algún tipo de parámetro. Para conseguirlo podemos ofrecer una aplicación de consola que interactúe con nuestro sistema. Esto, que puede sonar raro, es lo mismo que se hace en sistemas *nix: utilizar aplicaciones de línea de comandos relativamente simples que puedan llamarse unas a otras. Es una opción razonable y muchas veces resulta sencillo de utilizar para nuestros clientes.

Llegamos hasta donde llegamos

Este tipo de integración se adapta mejor a unos escenarios que otros y hay que ser consciente de ello.

Funciona muy bien cuando la comunicación entre los sistemas a integrar no requiere demasiada interacción y usa un “API” poco granular (coarse grained, que dirían por ahí), idealmente basada en datos más que en operaciones. El escenario prefecto es la típica importación de datos en modo batch entre sistemas.

Es posible serializar eventos/mensajes/comandos a ficheros y simular algo parecido a un bus o una cola de mensajes, con operaciones de tipo fire and forget, pero si estás en este caso, el sistema de ficheros empieza a quedarse corto y deberías considerar alternativas más apropiadas.

Incluso he visto (y padecido) sistemas de integración basados en ficheros que trataban de emular un protocolo request/response síncrono. Definitivamente, si tu integración requiere algo así, los ficheros de texto son una opción pésima.

Independientemente de lo bien o mal que se ajuste a tu escenario de integración, hay ciertas características del sistema de ficheros que imponen unas limitaciones que pueden ser importantes.

Suele ser complicado implementar bien el control de concurrencia. Al final vamos a tener dos o más procesos pegándose por acceder a los mismos archivos, y es necesario coordinarlos para evitar que se pisen, se lean ficheros incompletos o se borren antes de tiempo. No es imposible hacerlo bien, pero si se hace sin cuidado puede dar lugar a problemas difíciles de detectar y depurar (y ya sabéis que por desgracia tener cuidado al desarrollar suele ser la excepción más que la norma).

Unido a esto, no hay muchos sistemas de archivos transaccionales (y cuando lo son no se suelen aprovechar), por lo que no podemos garantizar la atomicidad de las operaciones y debemos estar preparados para que una ejecución del tipo “leer, procesar, renombrar” pueda verse interrumpida a medias, dejando el sistema de ficheros en un estado inconsistente. En el fondo es probable que estemos realizando implícitamente una transacción distribuida entre el sistema de archivos y la base de datos, y es imposible garantizar la integridad de la operación completa.

Para lidiar con ello es recomendable que el proceso de integración de cada fichero sea idempotente. Así, en caso de duda, si encontramos una operación a medio hacer, podemos repetirla desde el principio sin miedo. Esto no siempre es posible o fácil, y si tu aplicación no encaja con ello, tal vez te merezca la pena usar otros mecanismos de integración o correrás el riesgo de perder/duplicar información, lo que suele ser poco deseable (aunque también existen escenarios en los que es perfectamente admisible).

Otro punto importante es el rendimiento y la escalabilidad. Por muchas mejoras que se hayan introducido con el uso de SSDs y las cachés de los sistemas operativos, estamos tocando disco todo el tiempo y eso no es precisamente rápido. Si necesitas comunicar con 400 clientes cada 10 segundos, utilizar el sistema de archivos es una pésima idea. Por otra parte, si esos 400 clientes van a enviar información un par de veces al día y puedes procesarla con una tarea programada, utilizar un sistema de buzones basados en carpetas es una solución perfectamente válida.

Conclusiones

A veces cuando planteo una solución de integración basada en ficheros me miran con cara rara, como si fuese un ermitaño que no hubiese oído hablar de APIs REST o un vago que no quiere implementarlas y quiere ir a lo fácil.

En realidad, cuando elijo este tipo de soluciones es porque creo que las ventajas de usabilidad son importantes para el escenario que estoy tratando. De hecho, como hemos visto en este post, implementar correctamente un sistema de integración basado en ficheros tiene más dificultad de lo que aparenta para gestionar correctamente la concurrencia y la (falta de) transaccionalidad, por lo que probablemente muchas veces sea más sencillo a nivel de desarrollo implementar un API Web simple.

Sin embargo, ese esfuerzo extra a la hora de implementarlo redunda en unos menores costes operacionales de despliegue, mantenimiento, monitorización y uso, por lo que puede merecer (y mucho) la pena.

También es importante tener en cuenta el perfil de los usuarios potenciales de tu sistema de integración.

No es lo mismo diseñar un sistema de integración para gente con conocimientos limitados de informática “clásica” que diseñarlo para ser usado por los hipster de Javascript. Todavía hay mucha gente que se apaña bien para hacer una aplicacioncilla que lea y escriba ficheros en Visual Basic, pero empezar a hablar de peticiones HTTP y cabeceras de autenticación les resulta complicado.

Tampoco es lo mismo desplegar y monitorizar el sistema para el equipo de TI de una gran empresa con infinidad de sistemas de control centralizados, que para el administrativo de una PYME que bastante tiene con llegar a una carpeta y ver si hay archivos dentro o no.

Como siempre que se valoran temas de usabilidad, hace falta pensar en los futuros usuarios y no existen verdades absolutas.

En definitiva, huir del “másmolismo” (gracias Javi por la palabra) es importante. Que algo este de moda o demodé no debería ser nunca el factor técnico fundamental a la hora de decidir utilizarlo o no. Es mejor dejar los prejuicios a un lado y analizar las cosas como lo que son.

No hay posts relacionados.

» 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