Weblogs Código

Poesía Binaria

Cómo convertir una cantidad de segundos a horas, minutos y segundos en Bash

July 27, 2016 08:39 AM

photo-1415604934674-561df9abf539

El título del post lo dice claro. Imaginemos que tenemos un número grande, por ejemplo 58492, y que ese número son los segundos que ha tardado una tarea en realizarse, o el tiempo que ha pasado desde un evento determinado, pero a nosotros como humanos, este número no nos dice nada, lo entenderíamos mejor si lo expresamos como 16:14:52 como en todo, tenemos varias formas de hacerlo y voy a proponer dos.

Usando el comando date

La primera de ellas, me viene a la cabeza como más sencilla, ya que el mismo comando se encarga de hacaerlo, sólo tenemos que formatear la información de la manera debida. Por ejemplo:

$ date -u +%H:%M:%S –date=’@58492′

En este caso, le estamos diciendo que trabaje en GMT (-u), con esto, nos ahorramos que transforme la hora a la zona horaria en la que estamos actualmente, ya que puede variar el tiempo, y como estamos hablando de tiempo transcurrido, esto no depende de la zona horaria. Luego le pasamos el formato de salida con +”Formato” diciéndole cómo queremos que exprese la salida. Y por último la cantidad de segundos, precedido de arroba (@) para indicar que sólo son segundos.

Así podemos hacer también lo siguiente:

$ date -u +”%H horas, %M minutos y %S segundos” –date=’@58492′
16 horas, 14 minutos y 52 segundos

Aunque quedaría un poco raro cuando diga 1 horas, 0 minutos y 0 segundos en lugar de 1 hora. Eso sería mucho más elaborado, implicaría parsear la salida de date (con awk por ejemplo) y sería más trabajoso.

Una función para Bash

Pero también nos podemos crear una función específica para esto. Al fin y al cabo, se trata de hacer divisiones sobre la cantidad de segundos y desde hace años, Bash es muy bueno en matemáticas (si le pasamos la entrada como se la tenemos que pasar, y no le decimos nada raro.

1
2
3
4
5
6
7
8
function seconds_to_time()
{
    local TIME="$1"
    local h=$(($TIME/3600))
    local m=$((($TIME%3600)/60))
    local s=$(($TIME%60))
    printf "%02d:%02d:%02d\n" $h $m $s
}

Y luego la llamamos así:

$ seconds_to_time 58492
16:14:52

Lo malo, es que la función es específica para esto, y no hará nada extra (como hace time). Lo bueno es que podemos personalizar la salida como queramos (podemos meter sentencias condicionales para crear una salida compleja tipo “1 hora y 14 minutos”. Además, llamar a esta función es mucho más rápido que hacerlo con date. Sólo decir que date implica una ejecución que es lento (carga en memoria, creación de proceso, cambios de contexto, etc, mientras que con la función no hay que hacer nada de eso, ya que no salimos del mismo proceso Bash que se encarga de interpretarlo. Además, tanto las operaciones matemáticas como printf son órdenes internas de Bash.

Foto principal: Ales Krivec

The post Cómo convertir una cantidad de segundos a horas, minutos y segundos en Bash appeared first on Poesía Binaria.

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

Adrianistán

Subir APK a Google Play automáticamente

July 26, 2016 04:00 PM

Hoy vamos a ver como subir un APK a Google Play directamente, usando la línea de comandos, sin pasar por el navegador. Perfecto para automatizar la publicación de actualizaciones.

desarrollador-android-680x394

 

Google Play, como muchos otros servicios de Google, dispone de una API. Gracias a ella podemos interactuar con los servicios de Google directamente desde nuestro código. Para ello necesitaremos un Client ID, un Client Secret y un refresh_token. En mi entrada sobre como crear credenciales OAuth2 para Google sabras como hacerlo. Busca la API de Google Play Android Developer API y actívala. Anota el client ID, el client secret y el refresh_token. Además es necesario que entres a la consola de desarrollo de Google Play y autorices tus credenciales en Configuración -> Acceso a API

screenshot-play google com 2016-07-26 17-23-56

Ahora podemos pasar a escribir nuestro script de subida de APK. Para ello usaremos Node.js y el fantástico paquete googleapis, que se encuentra en npm.

Iniciar sesión en googleapis

Primero instala el paquete con npm.

npm install googleapis

Teniendo a mano el client ID, el client secret y el refresh_token podemos iniciar sesión.

var CLIENT_ID="";
var CLIENT_SECRET="";
var REFRESH_TOKEN="";

var fs = require("fs");
var google = require("googleapis");
var androidpublisher = google.androidpublisher("v2"); // Esta es la API que vamos a usar, AndroidPublisher v2
var OAuth2 = google.auth.OAuth2;

var auth = new OAuth2(CLIENT_ID,CLIENT_SECRET,"");
auth.setCredentials({
	refresh_token: REFRESH_TOKEN
});

auth.refreshAccessToken(function(err,tokens){
	google.options({auth: auth});
	// Ya estamos dentro
});

Crear y publicar una edición

¿Cuál es el procedimiento para subir un APK a Google Play usando la API?

  1. Crear una nueva edición
  2. Subir un APK a esa edición
  3. Asignar el APK a algún canal de distribución
  4. Guardar y publicar los cambios de la edición

Cuando creemos una edición se nos dará un ID de edición. Lo usaremos en el resto de llamadas. También cuando subamos un APK nos darán un versionCode que usaremos para asignar a uno de los canales de distribución. Google Play dispone de 4 canales de distribución:

  • alpha: Solo para los usuarios que hayas autorizado explícitamente
  • beta: Solo para los usuarios que hayas autorizado explícitamente
  • production: La versión que tienen todos los usuarios que no están registrados en alpha y beta.
  • rollout: Similar a production. Se indica un procentaje y Google se encargará de que solo el porcentaje de usuarios indicado tengan la actualización. Es ideal para probar nuevas características sin involucrar a todos tus usuarios.

Finalmente el código final del script queda así.

var CLIENT_ID="";
var CLIENT_SECRET="";
var REFRESH_TOKEN="";

var fs = require("fs");
var google = require("googleapis");
var androidpublisher = google.androidpublisher("v2");
var OAuth2 = google.auth.OAuth2;

var auth = new OAuth2(CLIENT_ID,CLIENT_SECRET,"");
auth.setCredentials({
	refresh_token: REFRESH_TOKEN
});

auth.refreshAccessToken(function(err,tokens){
	google.options({auth: auth});
	androidpublisher.edits.insert({
		packageName: "ga.yayeyo.cronoquiz.historia"
	},function(err,edit){
		console.dir(err);
		androidpublisher.edits.apks.upload({
			packageName: "ga.yayeyo.cronoquiz.historia",
			editId: edit.id,
			uploadType: "media",
			media: {
				mediaType: "application/vnd.android.package-archive",
				body: fs.createReadStream("platforms/android/build/outputs/apk/android-release.apk")
			}
		},function(err,req){
			console.dir(err);
			var versionCode = req.versionCode;
			androidpublisher.edits.tracks.update({
				packageName: "ga.yayeyo.cronoquiz.historia",
				editId: edit.id,
				track: "production",
				resource: {
					versionCodes: [versionCode]
				}
			},function(err,req){
				console.dir(err);
				androidpublisher.edits.commit({
					packageName: "ga.yayeyo.cronoquiz.historia",
					editId: edit.id
				},function(err,req){
					console.dir(err);
				});
			});
		});
	});
});

La entrada Subir APK a Google Play automáticamente aparece primero en Blog - Adrianistan.eu.

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

Adrianistán

Crear credenciales OAuth2 para Google

July 26, 2016 03:50 PM

Google dispone de muchas APIs a nuestra disposición de forma gratuita. Para poder usarla es necesario que hayamos iniciado sesión. Desde un navegador como Firefox o Chrome es muy sencillo, introducimos nuestro usuario y contraseña y ya podemos acceder a Gmail, YouTube, etc. Si queremos hacerlo desde nuestro propio programa también es posible, pero el procedimiento es distinto. Una vez hagamos esto podremos hacer lo mismo que haría un usuario, lo único que programándolo (leer los contactos, leer el correo, modificar suscripciones de YouTube, publicar en Blogger, etc).

Crea un proyecto de API

Crea un proyecto de Google y crea unos credenciales nuevos en la consola de APIs de Google.

screenshot-console developers google com 2016-07-26 14-52-43

Los credenciales pueden ser de varios tipos: clave de API, ID de OAuth y cuenta de servicio. La clave de API solo funciona en algunas API, el ID de OAuth es el sistema universal y la cuenta de servicio es para APIs de desarrollo. Creamos un ID de OAuth, pues es el método más universal.

screenshot-console developers google com 2016-07-26 16-29-16

Dependiendo de dónde opere nuestra aplicación tendremos un método u otro. El método Web requiere disponer de un servidor web y es el sistema óptimo si tenemos una aplicación Web (por ejemplo, tenemos una red social y queremos una opción de leer contactos de Gmail, usaríamos Web, pues es el procedimiento más cómodo). Si tu programa no tiene un servidor web detrás (es un programa normal de Windows, una herramienta de línea de comandos, un plugin de Firefox,…) usa Otro. Voy a explicar como funciona Otro.

screenshot-console developers google com 2016-07-26 16-30-31

Obtendremos un ID de cliente y un secreto de cliente. Guardamos esas dos claves, son muy importantes. Ahora vamos a activar las APIs. A la izquierda accedemos a Biblioteca. Buscamos la APIs que queramos usar y las activamos.

screenshot-console developers google com 2016-07-26 16-36-27

Obteniendo un access_token y un refresh_token

Para poder llamar a la API de Google es necesario tener un access_token. Los access_token caducan a las pocas horas. Para poder seguir realizando operaciones en la API pasado ese tiempo necesitamos un refresh_token. El refresh_token sirve exclusivamente para pedir un access_token nuevo. Este no caduca, pero no garantiza que siempre nos devuelva un access_token pues puede que el usuario haya decidido eliminar el permiso a nuestro programa.

El procedimiento de OAuth2 es el siguiente:

  1. Nuestra aplicación quiere autenticar al usuario en Google
  2. Nuestra aplicación genera una URL especial que debe abrir el usuario en el navegador
  3. En esa URL inicia sesión en Google el usuario, igual que si fuera a iniciar sesión en Gmail
  4. Se le informa al usuario de los permisos/APIs que nuestro programa quiere usar
  5. El usuario acepta o rechaza
  6. Si acepta ocurren varias cosas dependiendo del método. Si elegiste Otro se mostrará un código que deberás copiar en la aplicación.
  7. Ese código la aplicación lo envía a Google junto a nuestros datos de client ID y client secret.
  8. Google nos envía el access_token y el refresh_token.

Elaborar la URL de inicio de sesión

En la URL de inicio de sesión figuran varios datos.

  • El tipo de petición
  • Los permisos o APIs a las que queremos acceder (scopes)
  • Nuestro ID de cliente
  • Y la URL de redirección, que en nuestro caso, es especial.

En este ejemplo vemos una petición, con dos APIs o permisos, Chrome WebStore y Google Play.

https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.googleapis.com/auth/chromewebstore+https://www.googleapis.com/auth/androidpublisher&client_id=AQUI EL ID DE CLIENTE&redirect_uri=urn:ietf:wg:oauth:2.0:oob

Validar el código

El usuario nos dará el código que le haya dado Google. Nosotros ahora para obtener el access_token y el refresh_token tenemos que enviarlo de vuelta. En Curl el comando sería el siguiente:

curl "https://accounts.google.com/o/oauth2/tken" -d "client_id=AQUI EL ID DE CLIENTE&client_secret=AQUI EL CLIENT SECRET&code=ESTE ES EL CÓDIGO QUE NOS DA EL USUARIO&grant_type=authorization_code&redirect_uri=urn:ietf:wg:oauth:2.0:oob"

La respuesta, en un fichero JSON, contendrá el access_token y el refresh_token. En las librerías de APIs de Google muchas veces basta con especificar el client ID, el client secret y el refresh_token, pues se encargan de pedir automáticamente el access_token.

La entrada Crear credenciales OAuth2 para Google aparece primero en Blog - Adrianistan.eu.

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

Koalite

Quinto aniversario

July 26, 2016 05:06 AM

Cinco años, no está mal.

No hubiera apostado por ello en julio del 2011 cuando empecé a escribir este blog, pero ya van 300 posts y, pese a lo que ha cambiado mi vida en estos años, todavía consigo sacar un rato para escribir un post casi todas las semanas.

Lo malo de cumplir años es que uno se viene arriba y se marca metas ambiciosas, como llegar a esos 10 años del que sigue siendo mi ejemplo a seguir (gracias José María Aguilar).

Ya veremos si lo consigo, de momento vamos a por el sexto.

Una mirada atrás

Releyendo el post de aniversario el año pasado, creo que la tendencia que mencionaba de posts más largos y autocontenidos se mantiene. Quizá demasiado, porque a veces deben de resultar más que pesados. Añadiré a mi lista de buenos propósitos decorarlos más con imágenes y dibujillos para engañaros y que parezcan más fáciles de leer.

Otro cambio, que además comentaba en twitter hace poco, es el cambio de foco de .NET hacia otras plataformas (Javascript, Clojure) y, sobre todo, temas más genéricos sobre desarrollo.

Es comprensible.

Por una parte, las novedades de .NET no me interesan demasiado y que cada vez empleo más otros lenguajes. Por otra, me resulta más divertido y enriquecedor escribir sobre cosas genéricas que se prestan a discusión que repasar el último API de la librería de turno.

Y, por fin, vacaciones

Siguiendo con la tradición, en un alarde de optimización de posts aprovecho el post de aniversario para celebrar uno de los momentos más esperados del año: mis vacacaciones.

Vale, puede que tú no lo estuvieses esperando, pero créeme que yo llevo ya varias semanas contando los días para poder descansar un poco.

Hasta septiembre esto se quedará bastante parado. Pero nos conocemos, y como siempre, no podré resistirme a seguir quejándome de cosas en twitter.

¡Disfrutad del verano!

pescaito-frito

Este apetitoso plato de pescaíto frito no lo he preparado yo, está sacado de Spirit Sherry.

No hay posts relacionados.

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

Blog Bitix

No, un tag JSP o un tag de Grails no es equivalente a un componente de Tapestry

July 25, 2016 10:00 AM

Alguna vez que he dado una presentación sobre Apache Tapestry después de la misma me comentaron que eso mismo que explicaba se podía hacer con el framework que esa persona usaba. En un proyecto la tecnología no es es lo más importante pero es una herramienta que puede facilitar en gran medida el desarrollo. Respecto a los componentes de Tapestry alguien puede pensar que son iguales a los tag que existen en las tecnologías de presentación como JSP o Grails. En este artículo comentaré algunas diferencias importantes que los hace más y muy interesantes junto con otras características de framework.

Apache Tapestry
Java

Viendo el panel Kanban de la herramienta de peticiones JIRA que usamos para registrar y priorizar las siguiente tareas en la empresa que trabajo hay unas cuantas que consisten en dado un listado de compras poder realizar operaciones sobre múltiples filas sin salir de la pantalla del listado. La necesidad es evitar que los usuarios de la aplicación hagan las acciones de forma individual de forma repetitiva, evitarles esto harán que sean más productivos y podrán desarrollar su trabajo mejor y más rápido. Así de sencillo, aparentemente.

Esta necesidad que en la realidad será implementada con Grails quería compararla con una implementación equivalente usando Apache Tapestry porque como en muchas otras necesidades intuyo que con Tapestry implementarlas es significativamente más sencillo y con un resultado de implementación como en este caso con el que quedo más a gusto.

La necesidad

Definiendo más la necesidad hasta ahora cada fila del listado tiene un conjunto de botones para realizar acciones individuales y ahora se quiere al final del listado otro conjunto de botones para realizar acciones sobre las compras que se seleccionen de forma múltiple. Para seleccionar las compras se usará un checkbox colocado al inicio de cada fila. Para algunas acciones el usuario ha de introducir información adicional cosa que hará con un diálogo modal que ya existe pero que hasta ahora solo permitía hacer la acción de forma individual. Las mismas acciones se realizarán en varias páginas de la aplicación (después de la acción se deberá volver a la página en la que se estaba), solo se podrán hacer las acciones múltiples si en todas las compras seleccionadas es posible realizar esa acción y el contenido de los diálogos solicitando información adicional podrán depender de las compras seleccionadas. Las acciones en el ejemplo serán habilitar o deshabilitar. Determinar las acciones posibles de una compra es compleja y saber si una acción es posible no depende solo de información en la propia compra sino de otras entidades del sistema, en el ejemplo no será así pero se tendrá en cuenta en la implementación.

Esta sería una imagen del prototipo de los botones para hacer acciones múltiples, seleccionar compras y el diálogo modal para introducir información adicional.

Listado y modal de la necesidad expuesta

En la necesidad real las filas son compras pero en el ejemplo usaré la entidad Product. Las acciones en el ejemplo serán habilitar para la que no será necesaria información adicional, la otra acción será deshabilitar para la que se necesitará introducir una razón con un modal.

Las posibilidades

Para implementar técnicamente el que solo se puedan hacer las acciones múltiples según los productos seleccionadas se habilitarán o deshabilitarán los botones con JavaScript sin peticiones AJAX adicionales al servidor para ello toda la información necesaria deberá estar en el cliente. En este caso bastará habilitar o deshabilitar cada botón según si esa acción es posible en todas los productos seleccionadas pero eso podría no bastar ya que se podría requerir que productos fuesen del mismo vendedor. En el ejemplo con un atributo en un elemento HTML de la fila que contenga las acciones posibles separadas por comas bastará. De esta forma no habrá que hacer consultas adicionales al servidor mediante peticiones AJAX en cada nueva selección.

Sin embargo, como el contenido de los diálogos depende del producto o productos seleccionadas se hará una petición AJAX para obtener su contenido. De esta forma el contenido de los diálogos no tendrá que estar precargado (el número de acciones podría ser una decena) en el cliente ni generarlo con JavaScript en cliente que sería algo más complicado que usar la propia tecnología para generar contenido que está presente en el servidor y posiblemente más propenso a errores por usar JavaScript.

La implementación con Apache Tapestry

Definida la necesidad y unas pocas notas voy a poner el código de como con Apache Tapestry implementar la solución. La página del listado será la siguiente. En el checkbox de selección se añade el atributo data-product-actions con las acciones posibles que se obtienen del servicio AppService con el método getAvaliableActions. El componente de Tapestry actions generará el código de los botones tanto para los individuales en su uso <t:actions> con el parámetro product como múltiples en su uso con el parámetro type.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/Index.java">Index.java</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/Index.tml">Index.tml</pre></a></noscript>

El código para mostrar las acciones con botones para un determinado producto o para los productos es el siguiente. El mismo componente se encargará de realizar en el servidor la acción habilitar que no necesita modal. Con un poco de JavaScript, jQuery y Underscore se habilitarán o deshabilitarán los botones y se mostrará el diálogo para la acción deshabilitar.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/Actions.java">Actions.java</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/Actions.tml">Actions.tml</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/actions.js">actions.js</pre></a></noscript>

El código del modal para deshabilitar sería el siguiente. En el método show recibe los ids de los productos a deshabilitar y recupera del servidor el contenido de diálogo con una petición AJAX. El componente del modal se encargará de hacer el deshabilitado de los productos y la recarga de la página si finaliza correctamente o de mostar los errores de validación que se produzcan si no se ha introducido el motivo.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/DisableProductsModal.java">DisableProductsModal.java</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/DisableProductsModal.tml">DisableProductsModal.tml</pre></a></noscript>
<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/11715e0799339d0335a30ce30a17ee58/raw/modals.js">modals.js</pre></a></noscript>

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

Algunas diferencias con Servlets/JSP y Grails

La tecnología de presentación de páginas web Java con Java Server Pages o JSP permiten encapsular con un tag la generación de un trozo de HTML no en el propio JSP sino en ese tag que en código Java pudiendo incluir la llamada a un JSP. Los tags y librerías de tags son una forma de reutilizar esas partes de generación de código en el mismo proyecto y entre proyectos. Los tags además son una forma de abstraernos del funcionamiento interno del tag haciendo que solo necesitemos conocer sus parámetros.

Si usamos JSP usar librerías de tags es una buena idea, sin embargo, tiene algunas limitaciones como que requieren un archivo descriptor en formato XML que las defina y aunque pudiendo saber que parámetros definen y cuáles son requeridos no define el tipo de los parámetros que requiere. Los archivos XML en la época actual han caído en desuso porque son propensos a errores, errores que no son detectados hasta tiempo de ejecución, de los peores tipos de errores. Por otro lado, que los tags no especifiquen el tipo de parámetro que requiere cada uno hace que debamos inspeccionar el código fuente del tag con lo que la ventaja de abstraerse del funcionamiento no es del todo completa. Si por algún cambio el tipo de parámetro cambia hay que adaptar todos los usos del tag, si alguno no se hace nuevamente se producirán errores en tiempo de ejecución.

Grails usa GSP, una tecnología de presentación similar a los JSP. También dispone de tags que no requieren definir los tags en un archivo XML simplificando su uso pero que igualmente adolecen de algunos problemas como los JSP. Por un lado, los tags de Grails no disponen un mecanismo para hacer requerido un determinado parámetro con lo que deberemos incluir la comprobación con código nosotros, tampoco define el tipo de parámetros que requiere. También aunque hacer más simple su desarrollo al no tener un descriptor XML como en los tag JSP hace que haya que inspeccionar el código fuente para saber qué parámetros tiene, si son requeridos y cuál es el tipo del parámetro. Todo esto hace que puedan producirse errores en tiempo de ejecución y errores que no son producidos hasta que se ejercita el tag con un mal uso o un uso desactualizado al igual que usando los tag JSP.

En Apache Tapestry todo son componentes, las páginas también son componentes con la característica de que no están embebidos en otro componente. Un componente de Apache Tapestry sería similar a un tag de JSP o un tag de Grails, con ciertas similitudes pero no iguales en aspectos importantes. De pronto, un componente de Tapestry define los parámetros que necesita y si son requeridos pero también define el tipo del parámetro. Como se aprecia en las páginas de documentación de los componentes integrados de serie en Apache Tapestry se puede conocer esta información sin necesidad de conocer el código fuente del componente, documentación que podemos generar para los componentes que nosotros desarrollemos. Los parámetros, si son requeridos y sus tipos forman el contrato del componente y es lo único que deberemos conocer para usarlos, su funcionamiento interno nos es irrelevante que incluye el código JavaScript que necesite, podría que CSS y literales internacionalizados.

Pero esas no son las únicas diferencias con los tags de JSP o de Grails y es que estas son solo tecnologías de presentación, la V del patrón MVC. Los componentes de Tapestry aparte de encapsular la lógica de presentación también pueden encapsular lógica de controlador, en el conocido patrón MVC además de V pueden ser C con lo que encapsulan aún más funcionalidad. La lógica de presentación y controlador en los JSP y Grails está separada pero ambas lógicas no son independientes, están relacionadas, en Tapestry está encapsulada en el mismo componente.

Los componentes de Tapestry usan el modelo pull en vez del modelo push haciendo innecesario construir un objeto Map que pasar a la vista, haciendo que sea la plantilla la que solicite al controlador los datos que necesita y haciendo que el controlador no sepa que datos necesita la vista. El controlador solo deberá tener las propiedades y métodos que necesite la vista. Dado que en las plantillas tml de la vista no se pueden incluir expresiones complejas hace que no contengan lógica que estará en el controlador asociado que es código Java donde tendremos la ayuda del compilador para detectar errores.

Para volver a la misma página en Spring MVC, Struts o Grails posiblemente deberíamos recibir además información para retornar a la misma página en la que estabamos cosa que es innecesaria en Tapestry por su concepto de contexto de activación de página y el patrón Redirect-After-Post hará que al recargar la página por código con window.localtion.reload(); después de una petición POST el navegador no muestre un diálogo modal informando al usuario de que se reenviarán datos.

Diálogo recargar después de petición POST en Firefox

React y Polymer son tecnologías de cliente en algunos aspectos similares a los componentes de Apache Tapestry pero con la diferencia de que unos son para el navegador del cliente y otros para el servidor, nada nos impide en la misma aplicación usar en el cliente React y Polymer y en el servidor Apache Tapestry. Nótese en el código del caso anterior que Tapestry ofrece integración con JavaScript de un modo que no existe ni en Spring MVC, Struts o Grails e incorpora de serie RequireJS, Undercore y jQuery, un componente de Tapestry puede requerir la cargar de un recurso de JavaScript y desde el componente es posible pasar datos al JavaScript usando el servicio JavaScriptSupport.

Esto es solo un pequeño ejemplo de las posibilidades de Apache Tapestry me dejo muchas otras como los eventos, translators, encoders, coerces, librerías de componentes, inversion of control, AJAX, validaciones de formularios, … En un proyecto las herrramientas no son lo más importante pero el lenguaje de programación, framework y librerías importan, hay 10 razones para seguir usando Java y varios motivos para elegir Apache Tapestry.

Finalizando

Lamentablemente hasta el momento no he tenido una oportunidad laboral de comprobar y demostrar que como en este ejemplo pero basado en una necesidad real que con Tapestry la implementación de la solución es más sencilla, menos propensa a errores y que la productividad no está relacionado con escribir unas pocas líneas de código menos con un lenguaje menos verboso o dejar de escribir puntos y comas al final de las líneas, más aún con las novedades de Java 8. Quizá un día llegue esa oportunidad :|.

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.


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

Poesía Binaria

Cómo localizar ese script en PHP que no para de enviar spam…

July 25, 2016 08:35 AM

photo-1424296308064-1eead03d1ad9

Si administras un servidor en el que hay varias webs alojadas, tal vez te hayas encontrado alguna vez con envíos de correos indiscriminados en el servidor. O tal vez, tu proveedor de hosting te haya avisado de actividad maliciosa o ilegal por tu parte. Muchas veces se trata de un script malicioso que se ha instalado en el servidor que utiliza la función mail() de PHP, que a su vez llama al programa sendmail del sistema para el envío masivo de correos electrónicos.

Posibles causas de la infección

Puede ser porque:

  • No nos hemos dado cuenta y nos hemos descargado una aplicación con malware.
  • Una aplicación de nuestro servidor es vulnerable y alguien con malas intenciones ha podido subir un archivo en el sistema, tanto a través de un script como por FTP o utilizando cualquier otra técnica.
  • Alguna de las aplicaciones de nuestro servidor ha sido vulnerable y el código malicioso se ha propagado a todos los directorios visibles.

Lo bueno es que, si el script es accesible por web, los privilegios con los que se ejecutará el código malicioso serán los que tenga el usuario del servidor web (www-data, por ejemplo), por lo que si nuestro servidor está bien configurado, dicho programa no podrá acceder a sitios a los que el usuario del servidor web no pueda, y eso es bueno. Al menos, no podrá acceder fácilmente a ciertas contraseñas, archivos de sistema, incluso propagarse por scripts de otros usuarios.

Puede hacer muchas más causas, el caso es que el código malicioso suele estar cifrado, y el archivo se descifra siempre que se ejecuta, como esto que pongo a continuación (hay muchos scripts como este, más o menos dañinos, pero la forma suele ser parecida):
Screenshot 17-07-2016-210706

Casi siempre, el único cometido del script es propagarse por el servidor y enviar correo basura desde la máquina actual.

Repercusiones de todo esto

Bueno, no está bien que alguien coja nuestro ordenador para sus fines y si utilizan nuestro servidor para enviar correo, normalmente notaremos:

  • Menor rendimiento en el servidor (tanto CPU como ancho de banda)
  • Los archivos de log crecerán mucho
  • En ocasiones recibiremos mucho más correo no deseado de lo normal
  • Puede que nuestro proveedor de hosting contacte con nosotros en todo amenazante (por si somos nosotros los que lo causamos)
  • Si tenemos un servidor de correo legítimo en dicha máquina, los mensajes que se manden desde ahí poco a poco serán detectados como no deseados por los demás servidores.

Lo malo, es que si hay varias decenas de aplicaciones web en el servidor, es muy difícil detectar dónde está el problema (porque se pueden llevar a enviar varios mails por segundo y afectar a todas las webs de un servidor).

Medidas que podemos tomar: Desactivar el correo saliente

Si utilizamos postfix, podemos tomar una medida preventiva, para evitar que sigamos haciendo daño enviando correo no deseado. También es verdad que con esto evitaremos que se envíen correos originarios del servidor web por lo que tal vez otras webs también se vean afectadas por esto. Aunque es una buena medida para evitar un mal mayor.
Para este ejemplo el usuario del servidor web es www-data. Debemos editar /etc/postfix/main.cf y añadir al final la siguiente línea:

authorized_submit_users = !www-data, static:all

Tras esto, reiniciamos el servidor postfix. Al menos evitamos que se sigan enviando correos. Ahora deberíamos ver que Apache en su log de errores tendrá muchos errores como este:

sendmail: fatal: User www-data(33) is not allowed to submit mail

Medidas que podemos tomar: pasar un antivirus

Si disponemos de él, nunca está de más pasar un antivirus por nuestro servidor, ya que son capaces de detectar varios tipos de amenazas, incluso scripts maliciosos, aunque otras veces esto no tiene ningún efecto. Yo he probado con clamav, es importante tenerlo actualizado. Fue capaz de detectar muchos de los scripts maliciosos que había alojadas, aunque no todos.

$ clamscan -r –quiet /ruta/a/explorar/

Si no dice nada, no hay nada, cuando se queja es cuando encuentra cosas.

Medidas que podemos tomar: localizar el script malicioso

Para ello, debemos editar el archivo php.ini que utilice nuestro servidor (depende de si estamos utilizando módulo de Apache, CGI, FPM…), suelen estar en /etc/php/ , /etc/php5/ o rutas similares.
Ahí, podemos añadir al final unas líneas, o también buscar y modificar la configuración:

mail.add_x_header = On
mail.log = /tmp/phpmail.log

Con mail_add_x_header, el correo añadirá una línea X-PHP-Originating-Script en el que se indica qué script es el culpable. Y con mail.log escribimos un informe de todos los correos enviados por PHP. Este los podríamos ponerlo en /var/logs/ aunque tal vez el servidor web no tenga permiso para escribir ahí, por lo que podemos optar por crear un directorio en el que sí tenga permisos, o directamente ir a /tmp/ puesto que es una solución temporal.

Con esto, PHP generará un mensaje como este cada vez que se vaya a enviar un mensaje:

[24-Jul-2016 21:16:49 Europe/Berlin] mail() on [/ruta/del/script/nodeseado.php:2]: To: uncorreo@unserver.com — Headers:

Y con esta información, ya tenemos lo necesario para detectar dónde está el malo (o los malos). Sólo nos queda tomar las medidas pertinentes, borrar los scripts, aplicar cuarentena, etc.

Foto principal: Nicolai Berntsen

The post Cómo localizar ese script en PHP que no para de enviar spam… appeared first on Poesía Binaria.

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

Blog Bitix

Las cabeceras de cache del protocolo HTTP

July 24, 2016 08:00 PM

Establecer directivas de cacheo en los recursos devueltos en una página o aplicación web tiene las ventajas de reducir el número de peticiones que llegan al servidor mejorando la latencia y el rendimiento pudiendo atender a más usuarios y mejora los tiempos de carga de las páginas. Usando varias directivas de cacheo la aplicación es capaz de determinar cómo quiere que el contenido devuelto o los recursos sean cacheados por los clientes o servidores de cache intermedios.

HTML

Cachear aquella información que es costosa de generar y es muy solicitada consigue por un lado evitar que el servidor sea capaz de atender todo el tráfico reduciendo la cantidad de capacidad necesaria del servidor y por otro lado consigue que la información sea devuelta en menor tiempo. Para aquella información que no necesite estar completamente actualizada o que no cambia cada poco tiempo es candidata a cachearla en caso necesario. La cache se puede realizar en los navegadores guardando estos recursos como imágenes y hojas de estilos que consiguen reducir el número de peticiones al servidor y mostrando la página más rápidamente al usuario. La cache también se puede hacer en el lado del servidor usando soluciones específicas como Varnish, memcached o para los casos más habituales que serán la mayoría las funcionalidades incorporadas en el servidor web como en el caso de Nginx.

El cacheo o almacenamiento temporal de datos puede hacerse a diferentes niveles sin ser exclusivos y de diferentes tipos de información. En la base de datos, en la aplicación, a nivel de página, con servidor intermedio o en el cliente.

Según la cantidad de tiempo de expiración que establezcamos como cache para el contenido conseguiremos variar el número de aciertos en la cache, aumentando el tiempo unos pocos segundos el tiempo que almacenamos en la cache el contenido conseguimos aumentar el porcentaje de aciertos en mayor medida. Con un tiempo de cache de un minuto ya se consiguen porcentajes elevados de aciertos según el número de peticiones realizadas en ese periodo de tiempo.

Petición con acierto y fallo en la cache

En el protocolo HTTP 1.1 se definieron tres mecanismos para las caches:

  • Validez: permite usar un recurso sin hacer ninguna comprobación con el servidor ni para revalidarlo. Por ejemplo, la cabecera Expires indica en que momento el recurso puede haberse quedado obsoleto y se debería revalidar. La cabecera Cache-Control: max-age indica durante cuanto tiempo el recurso puede considerarse válido. Esto evita hacer peticiones al servidor si los recursos se consideran válidos.
  • Validación: una vez que un recurso se considera que puede ser obsoleto se debería comprobar haciendo una petición al servidor para conocer si sigue siendo válido y si no lo es obtener una nueva versión. Usando las cabeceras If-Modified-Since o Etag puede comprobarse si el recurso ha sido modificado con posterioridad a una fecha o ha variado. Se ha de hacer una petición para comprobar la validez del recurso pero los casos que sigan siendo válidos no hará falta descargarlos de nuevo. Si el recurso sigue siendo válido el servidor responde con el código de estado 304 y sin el contenido en la respuesta.
  • Invalidación: las peticiones que usen los métodos PUT, POST y DELETE pueden invalidar recursos ya que modifican el estado del servidor.

El servidor especifica como quiere que el contenido o recursos que devuelve sean cacheados a través de varias directivas del protocolo HTTP, establecidas como cabeceras en la respuesta cuando se solicita el contenido o recurso. Algunas cabeceras realizan funciones similares habiendo cierto solapamiento de funcionalidad. Son las siguientes:

  • Cache-Control: private | public, no-cache, no-store, max-age, s-maxage, must-revalidate, no-transform, proxy-revalidate
    • El valor private indica que el recurso es privado para el usuario y no debería ser cacheado. Esto no hace el recurso más seguro ya que la información no se transmite cifrada para ello hay que usar un protocolo seguro con TLS/SSL.
    • no-cache el recurso no debería ser cacheado.
    • no-store el recurso no debería ser almacenado.
    • max-age normalmente se ha usado la directiva Expires pero esta permite establecer el máximo tiempo especificado en segundos a cachear un recurso.
    • s-maxage similar a max-age pero para las caches intermedias entre el cliente y el servidor.
    • must-revalidate cuando un recurso se queda obsoleto no se debe usar sin antes validar contra el servidor si sigue siendo válido.
    • no-transform indica que el contenido original no debe ser modificado, por ejemplo, modificando el recurso para optimizarlo si por ejemplo se trata de una imagen.
    • proxy-revalidate lo mismo que must-revalidate pero para las caches intermedias.
  • If-Modified-Since: si el recurso solicitado con su variante no ha sido modificado con posterioridad a una fecha se devolverá un código de estado 304 sin el contenido.
  • Expires: es una marca de tiempo que indica cuando el recurso expira, dado que se basa en el tiempo no es muy precisa ya que los relojes de cada ordenador no están perfectamente sincronizados y hay variaciones incluso de minutos. Preferiblemente es mejor usar Etag o max-age.
  • Etag: entity-tag o etag es un código hash único del contenido que permite conocer si el recurso ha cambiado. Si el recurso no ha cambiado no hace falta devolver el recurso, si ha cambiado se devuelve en la misma petición. Al no depender de una marca de tiempo como Expires o max-age es más fiable.
  • Vary: indica que el recurso varía según alguna cabecera proporcionada por el cliente como por ejemplo User-Agent o Accept-Encoding.
  • Pragma: esta es una directiva antigua que indicada como pragma: no-cache, se interpreta como cache-control: no-cache.

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

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

Blink (by D.T.S.)

July 23, 2016 07:09 AM

Es inadmisible que después de hablar de AIN, o incluso de Ultra Compressor, mencionando la fuente de inspiración para mis programas, no haya tenido ocasión de hablar de BLINK. Sin nada que ver con Blinker, salvo que el nombre me gustaba, y el concepto de parpadeo era muy pertinaz, Blink surgió a principios de 1997, [...]

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

Jesús Perales

¿Como instalar Pokémon Go?

July 21, 2016 06:40 PM

¿Como instalar Pokémon Go?

Para instalar Pokémon GO en nuestro celular, aunque aun no este liberado oficialmente para nuestro país es necesario ir a la pagina de APK Mirror la cual podremos encontrar muy fácilmente entrando a Google y escribir pokemon go apk

¿Como instalar Pokémon Go?

Abrimos el primer resultado

¿Como instalar Pokémon Go?

Verificamos que sea la ultima versión dando click donde dice Pokemon GO (A fecha de hoy es la 29.3) y damos click o pulsamos sobre la versión mas reciente.

¿Como instalar Pokémon Go?

Bajamos a donde esta el botón rojo que dice DOWNLOAD APK

¿Como instalar Pokémon Go?

Damos click en descargar

¿Como instalar Pokémon Go?

Esperamos a que termine la descarga

¿Como instalar Pokémon Go?

Abrimos el APK , probablemente te aparecera el siguiente mensaje, damos click en configuración

¿Como instalar Pokémon Go?

Activamos la opción de orígenes desconocidos

¿Como instalar Pokémon Go?

Aceptamos

¿Como instalar Pokémon Go?

Vamos a la parte de descargas de nuestro celular

¿Como instalar Pokémon Go?

Volvemos a abrir la aplicación y damos click en instalar o actualizar si tenias una versión anterior

¿Como instalar Pokémon Go?

¿Como instalar Pokémon Go?

¿Como instalar Pokémon Go?

¿Como instalar Pokémon Go?

Ahora solo nos toca jugar

¿Como instalar Pokémon Go?

Nota:

Obviamente esto no sera necesario cuando el juego se libere oficialmente en tu país, en mi caso México.

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

Poesía Binaria

Cómo extraer ruta, nombre de fichero y extensión en Bash de forma nativa para nuestros scripts

July 20, 2016 08:21 AM

7959831794_e5699df68b_k
Bash tiene infinidad de opciones, y en los últimos años se ha extendido muchísimo y nos permite hacer cosas muy chulas. Aunque un sistema con tantas opciones como este, es también un poco lioso y difícil de aprender. Por eso en ocasiones viene bien una chuleta para realizar operaciones sencillas que pueden llegar a ser un poco rebuscadas como obtener el nombre de un fichero y su extensión.

Bash, al ser un intérprete de comandos de consola, una de sus principales funciones es trabajar con archivos y cuando queremos utilizar archivos, tenemos que jugar con sus posibles nombres, para ello, deberíamos poder extraer fácilmente la ruta de un archivo, su nombre y separarlo de la extensión. Por ejemplo, si queremos transformar varios archivos de un directorio de jpg a png, o hacer un script que trate de forma los archivos de imagen y de otra los vídeos, o incluso contar cuántos archivos tienen qué extensión…

Para ello, y a modo de autochuleta también, vamos a plantear varios ejemplos en los que se utilizan los modificadores # y % de expansión de parámetros en Bash:

1
2
3
4
5
6
7
#!/bin/bash
FICHERO="archivo.txt"
NOMBRE="${FICHERO%.*}"
EXTENSION="${FICHERO##*.}"

echo $NOMBRE
echo $EXTENSION

Si ejecutamos esto veremos que primero se mostrará el nombre (archivo) y luego la extensión (txt), aunque, esto deberíamos complicarlo un poco más, y plantearnos preguntas para verificar que funciona en todos los casos, por ejemplo, ¿qué pasaría si el archivo no tiene extensión? En este caso, devolvería el mismo nombre del archivo, y eso puede darnos problemas, para solucionarlo, plantearemos EXTENSION de la siguiente forma:

1
EXTENSION=$([[ "$FICHERO" = *.* ]] && echo "${FICHERO##*.}")

Ahora bien, ¿qué pasaría si nos pasan un archivo con doble extensión? Como “archivo.tar.bz2″, la variable NOMBRE contendría (archivo.tar) y EXTENSION contendría (bz2). Esto puede ser útil en ciertas ocasiones, por ejemplo, podríamos recorrer todas las posibles extensiones del archivo:

1
2
3
4
5
6
7
8
9
10
11
FICHERO="archivo.tar.bz2"
NOMBRE="${FICHERO%.*}"
EXTENSION="${FICHERO##*.}"

echo $EXTENSION

while [ -n "$EXTENSION" ]; do
   FICHERO="${FICHERO%.*}";
   EXTENSION=$([[ "$FICHERO" = *.* ]] && echo "${FICHERO##*.}");
   echo $EXTENSION;
done

Pero si queremos extraer todas las extensiones juntas del archivo, deberíamos hacer:

1
EXTENSION=$([[ "$FICHERO" = *.* ]] && echo "${FICHERO#*.}")

Y si queremos el nombre sin ninguna extensión:

1
NOMBRE="${FICHERO%%.*}"

Lo que variamos es el número de % y de # que colocamos. En este caso, si utilizamos # estaremos eliminando el prefijo de un patrón, es decir, la parte a la izquierda, y como eliminamos *. queremos decir que eliminamos todo lo que hay delante del punto, ahora si usamos un sólo # estaremos eliminando lo menor posible y si usamos dos, lo mayor posible. De esta forma ${FICHERO#*.} al eliminar poco, nos devuelve todas las extensiones y ${FICHERO#*.}, al encontrar el patrón más grande posible, muestra sólo la última extensión.

De esta forma, nos podemos crear nuestras propias funciones:

1
2
3
4
5
6
7
8
9
10
11
function my_filename()
{
  local FILE="$1"
  [[ "$2" = "1" ]] && echo "${FILE%.*}" || echo "${FILE%%.*}"
}

function my_extension()
{
  local FILE="$1"
  [[ "$FILE" = *.* ]] && ( [[ "$2" = "1" ]] && echo "${FILE#*.}" || echo "${FILE##*.}")
}

Así si hacemos varias llamadas:

$ my_filename archivo
archivo
$ my_filename archivo.tar
archivo
$ my_filename archivo.tar.bz2
archivo
$ my_filename archivo.tar.bz2 1
archivo.tar

$ my_extension archivo

$ my_extension archivo.tar
tar
$ my_extension archivo.tar.bz2
bz2
$ my_extension archivo.tar.bz2 1
tar.bz2

Incluyendo directorios

Si queremos que estas funciones separen también nombres de archivo de directorios tenemos varias opciones. Una de ellas es utilizar basename y dirname, que son dos comandos que muchas veces están disponibles para extraer nombres de archivos y nombres de directorios, así:

$ basename /usr/share/sane/xsane/archivo.tar.bz2
archivo.tar.bz2
$ dirname /usr/share/sane/xsane/archivo.tar.bz2
/usr/share/sane/xsane

Aunque si queremos una solución puramente hecha en Bash, y así evitar hacer llamadas externas y ganar algo de rendimiento podemos utilizar las mismas técnicas anteriores. Es más, primero, vamos a incluir rutas de archivo con las funciones anteriores, a ver qué pasa:

$ my_filename “/usr/share/sane/xsane/archivo.tar.bz2″
/usr/share/sane/xsane/archivo
$ my_extension “/usr/share/sane/xsane/archivo.tar.bz2″
bz2

En principio el nombre incluye la ruta completa, pero la extensión se comporta bien, aunque si el directorio contiene un punto también…

$ my_extension /etc/init.d/lm-sensors
d/lm-sensors

Por tanto, primero vamos a intentar eliminar las rutas, y extraer el nombre de archivo base. Esto lo podemos hacer basándonos en el patrón entre la última barra y el final del nombre, o lo que es lo mismo, eliminando desde el principio del nombre hasta la última barra:

$ echo “${FICHERO##*/}”

y para extraer la ruta solamente, podremos utilizar algo similar a la extensión:

$ $([[ “$FICHERO” = */* ]] && echo “${FICHERO%/*}”)

Esto mismo, lo podemos trasladar a las funciones, de la siguiente forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function my_filename()
{
  local FILE="$1"
  FILE="${FILE##*/}"
  [[ "$2" = "1" ]] && echo "${FILE%.*}" || echo "${FILE%%.*}"
}

function my_extension()
{
  local FILE="$1"
  FILE="${FILE##*/}"

  [[ "$FILE" = *.* ]] && ( [[ "$2" = "1" ]] && echo "${FILE#*.}" || echo "${FILE##*.}")
}

function my_dirname()
{
  local PATH="$1"

  [[ "$PATH" = */* ]] && echo "${PATH%/*}"
}

Con las que tendremos un acceso más fácil y amigable para nuestros scripts.

Foto principal: Christopher Adams

The post Cómo extraer ruta, nombre de fichero y extensión en Bash de forma nativa para nuestros scripts appeared first on Poesía Binaria.

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

Variable not found

Cerrado por vacaciones :)

July 20, 2016 07:23 AM

Parecía que no llegaban, pero por fin están aquí las vacaciones :)

Como suele ser habitual por esta época, colgaré temporalmente los hábitos digitales y durante las próximas semanas el blog quedará a merced de las olas. Aunque la mayor parte del tiempo seguiré trabajando en distintos temas, espero poder reducir un poco la intensidad y tomar aunque sea algunos días de descanso para poder disfrutar de la familia y darme algún que otro bañito relajante en las costas de Cádiz.

Espero que nos veamos a la vuelta, todos morenitos y con las pilas al 100%, ya en el mes de septiembre.

¡Felices vacaciones, amigos!

Atardecer en playa
Imagen: Atardecer en Costa Ballena (Rota-Cádiz)
Autor: Ramón Sobrino
https://trapatroles.wordpress.com/2015/04/08/rota-cadiz/

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

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

ZOHO | App

July 20, 2016 12:18 AM

Zoho CRM App Review - Cloud Based CRM Software http://flip.it/6jUod

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

Variable not found

Refrescar settings de aplicaciones ASP.NET Core sin necesidad de reiniciarlas

July 19, 2016 07:03 AM

ASP.NET CoreCuando ASP.NET “clásico” modificábamos algún setting de la aplicación almacenado en el archivo web.config, esto traía como consecuencia directa el reciclado del pool, o en otras palabras, la aplicación se reiniciaba irremediablemente para recargar los nuevos valores de configuración.

Pero como sabemos, en ASP.NET Core el sistema de configuración ha cambiado para mejor, y ahora los settings podemos almacenarlos en archivos JSON independientes del resto de configuraciones del proyecto, así como en otros formatos y ubicaciones. Y ya que se ponían con el tema, el equipo de ASP.NET ha aprovechado para hacer que podamos modificar los settings sobre los archivos de configuración y que éstos sean aplicados sobre la marcha, sin necesidad  de volver a arrancar la aplicación.


¿Y cómo conseguimos esto? Pues la verdad es que lo han puesto bastante sencillo, como podemos ver en la siguiente porción de código de la clase de inicialización de la aplicación:
public class Startup
{
   public Startup(IHostingEnvironment env)
   {
    var builder = new ConfigurationBuilder()
       .SetBasePath(env.ContentRootPath)
       .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
       .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

    // More startup code…
}
}
¡Así de fácil! Estableciendo a cierto el valor del parámetro reloadOnChange, cuando leamos los settings de nuestra aplicación mediante una instancia de IConfiguration los valores obtenidos siempre estarán frescos como lechugas :)

Por ejemplo, imaginemos el siguiente contenido en el archivo de settings:
{
"Title": "Probando, probando…"
}
Si accedemos a los parámetros de configuración como se muestra en el siguiente código, podremos observar que las peticiones a "/settings/title" retornan el valor especificado en el archivo de configuración, y aunque lo modifiquemos durante la ejecución, al realizar de nuevo la petición aparecerá actualizado sin necesidad de echar abajo la aplicación:
public class SettingsController : Controller
{
private readonly IConfiguration _configuration;

public SettingsController(IConfiguration configuration)
{
_configuration = configuration;
}
public IActionResult Title()
{
return Content(_configuration["Title"]);
}
}
Pero ojo, esto ocurre sólo cuando accedemos a los datos de forma no tipada, mediante la instancia de IConfiguration. En cambio, si utilizamos utilizamos la alternativa tipada basada en IOptions<T>, los datos no serán actualizados porque la instancia de nuestra clase de settings es cargada exclusivamente durante el arranque. Pero no os preocupéis porque seguro que podemos idear alguna forma sencilla de conseguirlo ;)

Un posible enfoque es registrar en el contenedor de una instancia singleton de la clase que represente nuestros settings, la misma que utilizamos con IOptions<T>, y asegurarnos de que sus valores cambian cuando el archivo de settings es modificado. De esta forma, siempre que un componente necesite acceder a los settings, tendrá la versión más actual de los valores.

El siguiente código simplificado de la clase de inicialización muestra cómo podríamos conseguirlo:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
// Add other settings providers

Configuration = builder.Build();

// (1) Creamos una instancia singleton de la clase de settings

MySettings = new MySettings() { Title = "Default title"};

// (2) Inicializar la instancia con el archivo de configuración
// Ojo, requiere el paquete Microsoft.Extension.Configuration.Binders

Configuration.Bind(MySettings);

// (3) Aseguramos que se recarga cuando cambia el archivo

var token = Configuration.GetReloadToken();
token.RegisterChangeCallback(c =>
{
// Actualizar la instancia tras cada cambio
Configuration.Bind(MySettings);
}, this);
}

// Instancia singleton para settings tipados
public MySettings MySettings { get; }

// Instancia singleton para settings sin tipo
public IConfigurationRoot Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
// (4) Registramos la instancia como singleton en el DI
services.AddSingleton<MySettings>(MySettings);

// Configure other services here...
}

public void Configure(IApplicationBuilder app,
                          IHostingEnvironment env,
                          ILoggerFactory loggerFactory)
{
// Configure the pipeline here
}
}

public class MySettings
{
public string Title {get; set; }
}
A grandes rasgos, lo que hacemos es:
  1. Creamos una instancia singleton de la clase que proveerá acceso tipado a los settings.
  2. La inicializamos desde el objeto de configuración mediante su extensor Bind(), disponible en el paquete Microsoft.Extensions.Configuration.Binders.
  3. Obtenemos un token de recarga de la configuración y lo usamos para definir una función callback que será invocada cuando el sistema detecte cambios en ella. En dicha función actualizamos la instancia singleton de la clase tipada de settings.
  4. Registramos la instancia en el contenedor de inyección de dependencias.
De esa forma, ya podremos acceder a la configuración fresca solicitando al sistema de inyección de dependencias nuestra instancia de settings tipados, por ejemplo:
public class SettingController : Controller
{
private readonly MySettings _settings;

public SettingController(MySettings settings)
{
_settings = settings;
}

public IActionResult Title()
{
return Content(_settings.Title);
}
}
Espero que os resulte de utilidad :)

Publicado en Variable not found.

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

Variable not found

Variables de sesión en ASP.NET Core 1.0

July 19, 2016 07:00 AM

ASP.NET CoreContinuamos lamiéndonos las heridas provocadas por los sucesivos terremotos que han supuesto las prereleases y releases públicas de ASP.NET Core, con objeto de ponernos al día en los cambios y novedades que vamos encontrando.

Hoy vamos a hablar de las variables de sesión, un tema que ya tratamos por aquí hace bastante tiempo, pero que conforme evolucionaba el producto ha cambiado lo suficiente como para dedicarle un nuevo post.

Como sabemos, simplemente decir que las variables de sesión permiten mantener en el servidor datos arbitrarios relativos al estado de un visitante durante su uso del sitio web. Aunque no están demasiado bien vistas por temas de limitación de escalabilidad y por atentar contra la naturaleza stateless de la web, sin duda son un recurso bastante utilizado porque simplifican muchos escenarios frecuentes de necesidades de mantenimiento de estado entre peticiones.

Básicamente, este mecanismo consiste en añadir a todos los visitantes una cookie  con un token que los identifica de forma única, lo que permite en el lado servidor mantener valores en un “diccionario” persistente exclusivo para cada usuario. Lo habitual es que este diccionario tenga un tiempo de vida limitado, normalmente coincidente el tiempo que el visitante pasa en nuestro sistema web.

En ASP.NET “clásico”, este mecanismo venía integrado de serie en el monolítico framework; podíamos desactivarlo o simplemente no usarlo, pero su código seguía estando ahí, acompañando a cada petición. Ahora, gracias a la modularidad de ASP.NET Core, las variables de sesión sólo se incluirán en nuestra aplicación si así lo decidimos de forma expresa.

1. Instalación de paquetes Nuget necesarios

Para utilizarlas, lo primero que debemos hacer es instalar el paquete Nuget Microsoft.AspNetCore.Session. Con esto ya tendremos disponibles los middlewares y las funciones básicas que necesitamos.

Ahora tenemos que pensar dónde queremos que se almacenen los datos de sesión; esta decisión es importante porque determinará la velocidad con la que el servidor leerá o escribirá datos e impondrá determinadas limitaciones de escalabilidad:
  • Si decidimos guardarlas en la memoria del servidor obtendremos la máxima velocidad a la hora de acceder a los datos, pero perderemos la capacidad de que la aplicación escale y pase a ejecutarse en varios servidores, granjas o distintas instancias. Por tanto, es la opción recomendable para aplicaciones pequeñas que van a ejecutarse en un único nodo, y en las que la persistencia de la información no sería importante (por ejemplo, si el servidor web se reinicia).
     
  • Si decidimos guardarlas en un almacén independiente perderemos algo de rendimiento, pero la información podrá ser compartida por los distintos servidores que ejecuten la aplicación, por lo que escala mejor, o al menos llegará hasta donde sea capaz de escalar el almacenamiento elegido. Es la opción recomendable para aplicaciones grandes que van a ser servidas desde distintos nodos por necesidades de rendimiento o tolerancia a fallos.
El sistema de sesiones de ASP.NET Core almacena los datos apoyándose en el sistema de caching del framework, que se distribuye en un paquete Nuget aparte. De esta forma, sólo tendremos que preocuparnos por instalar el paquete correspondiente, configurarlo apropiadamente, y el sistema de sesiones se encargará del resto.

Si decidimos utilizar la memoria del servidor para almacenar el estado de sesión, debemos instalar el paquete Microsoft.Extensions.Caching.Memory. En cambio, si preferimos opciones más apropiadas para entornos distribuidos, el framework ofrece de serie soporte para caching en Redis y SQL Server, que respectivamente se distribuyen en los paquetes Microsoft.Extensions.Caching.Redis y Microsoft.Extensions.Caching.SqlServer. En general, deberías elegir Redis si necesitáis alto rendimiento porque en este aspecto es muy superior a la opción SQL Server.

2. Configuración de componentes

Una vez instalado el paquete del sistema de caché elegido, como suele ser habitual, el siguiente paso sería añadir los correspondientes servicios al inyector de dependencias:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMemoryCache(); // Add in-memory caching services
services.AddSession(); // Add session services
...
}
El extensor AddMemoryCache() que estamos usando en el código anterior es el utilizado cuando usamos caché en memoria. En caso de usar Redis deberíamos sustituirlo por  AddDistributedRedisCache(), o por  AddDistributedSqlServerCache() en caso de usar SQL Server.

Todos ellos admiten un parámetro mediante el cual podemos configurar sus opciones, como cadenas de conexión, aspectos de su funcionamiento, etc. Por ejemplo, el siguiente código muestra una posible configuración del caché in-memory mediante la cual indicamos que deben realizarse comprobaciones de caducidad de entradas en la caché cada cinco minutos:
services.AddMemoryCache(opt =>
{
opt.ExpirationScanFrequency = TimeSpan.FromMinutes(5);
});
Tras añadir los servicios en el sistema de inyección de dependencias, ya sólo nos falta incluir el middleware SessionMiddleware en el pipeline de ASP.NET mediante una llamada al extensor UseSession() de IApplicationBuilder:
public void Configure(IApplicationBuilder app, 
IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseSession(); // Include session middleware
...
}

Ilustración del pipeline de ASP.NET Core con SessionMiddleware insertado antes del framework MVC

Obviamente, es importante añadir el middleware de gestión de sesiones antes que MVC u otros frameworks de mayor nivel que requieran utilizar este mecanismo.
Como en el caso anterior, UseSession()admite parámetros que permiten configurar determinados aspectos de su funcionamiento, como podemos observar en el siguiente ejemplo:

app.UseSession(new SessionOptions() 
{
CookieName = "MyAppSession",
CookiePath = "/",
IdleTimeout = TimeSpan.FromMinutes(30),
CookieHttpOnly = true
});

3. Establecer, obtener y eliminar variables de sesión

En primer lugar, es importante tener en cuenta que los pasos anteriores son obligatorios si queremos utilizar variables de sesión. Cualquier intento de utilización de las mismas sin haber instalado y configurado previamente los componentes que las proporcionan provocarán la aparición de un error:

Excepción produciéndose al intentar acceder a una variable de sesión sin haber configurado el sistema

El acceso a las variables de sesión se realiza utilizando la propiedad HttpContext.Session, es decir, la clase Controller no proporciona ya una propiedad Session como en versiones anteriores de MVC, aunque obviamente siempre podríamos crearnos un atajo sobre la propia clase o una de sus antecesoras. Por cierto, fijaos qué limpio queda usando las nuevas expresiones lambda en miembros de función de C# 6:
public class HomeController: Controller
{
public ISession Session => HttpContext.Session;

public IActionResult Index()
{
var homeIndexVisits =
1 + Session.GetInt32("HomeIndexVisits").GetValueOrDefault();

Session.SetInt32("HomeIndexVisits", homeIndexVisits);
return Content($"You visited this page {homeIndexVisits} times");
}
}
Los que habéis usado variables de sesión en versiones anteriores de ASP.NET, o incluso de ASP “clásico”, seguro que os han llamado la atención esos métodos GetInt32() y SetInt32() de la propiedad Session. Pues sí, esto otro cambio importante: hasta ahora podíamos introducir cualquier tipo de objeto en variables de sesión, y el abuso de esta capacidad ha generado muchísimos problemas. He llegado a ver incluso contextos de datos o conexiones a bases de datos mantenidas en memoria de esta forma, creando unos problemas terribles en tiempo de ejecución, y seguro que algunos os habréis encontrado incluso con cosas peores (aunque he de decir que no se me ocurre ninguna ;D)

Para evitar estas aberraciones, en ASP.NET Core sólo podremos guardar en variables de sesión objetos serializados, y esto se manifiesta en el tipo de dato que es ahora el diccionario Session: un array de bytes. Así se acabó el establecer o leer objetos completos directos como hemos hecho siempre, tendremos que serializarlos a un array de bytes para guardarlos el almacén de sesión, así como deserializarlos para poder acceder a ellos. De esta forma, simplificamos el uso de almacenamientos distintos a la memoria local para esta información, facilitando la escalabilidad y mejorando la fiabilidad.

Por tanto, la única fórmula “oficial” para obtener y almacenar datos de sesión pasan por usar los métodos Get() y Set(), que operan exclusivamente con un array de bytes. Sin embargo, el framework ofrece adicionalmente los extensores GetInt32(),  SetInt32(), GetString() y SetString() para simplificar la operación con esos tipos de datos tan frecuentes. Un ejemplo para verlos en acción:
public class UserController : Controller
{
public ISession Session => HttpContext.Session;
public IActionResult Get()
{
var name = Session.GetString("name");
var age = Session.GetInt32("age");
if (name == null || age == null)
{
return Content("No valid user data");
}
else
{
return Content($"User {name} is {age}");
}
}

public IActionResult Set(string name, int age)
{
Session.SetString("name", name);
Session.SetInt32("age", age);
return Content("User data changed");
}
}
Si por cualquier motivo necesitamos guardar objetos o grafos más complejos (ojo, que ya os digo que no es conveniente por la repercusión que puede tener en consumo de recursos), podríamos crear un serializador personalizado capaz de convertir el objeto a array de bytes y viceversa, o bien utilizar algo más simple como JSON y guardarlo y recuperarlo como cadena de texto.

Finalmente, para eliminar variables de sesión tenemos dos opciones. La más violenta, utilizando el método Clear(), elimina todas las variables de sesión asociadas al visitante actual (aunque mantiene la cookie); otra, más selectiva, es utilizar el método Remove(), para eliminar sólo aquella cuya clave suministremos.

Ah, y una última observación: fijaos que no hay nada similar al Session.Abandon() que teníamos en versiones anteriores de ASP.NET, así como tampoco tenemos disponibles eventos como Session_Start() y Session_End()que podíamos capturar en el Global.asax cuando comenzaban o finalizaban las sesiones de usuario (por cierto, ni parece que los vaya a haber).

Publicado en Variable Not Found.

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

Poesía Binaria

Cómo aplicar filtros de GIMP a un vídeo

July 18, 2016 08:02 AM

photo-1464602083226-de2c1675e946

Estoy subiendo vídeos a mi canal personal de Youtube. Aunque sólo tengo algunos pequeños tutoriales, postales de navidad y cosas así. He decidido subir algunos vídeos y cortos antiguos. Aunque están grabados con una cámara analógica y en cinta magnética, lo que significa que el vídeo tiene mucha pérdida y poca calidad. En un intento por darle algo más de definición, o al menos que el visionado sea algo más agradable, pensé en un filtro de Gimp (Desenfocado Gaussiano Selectivo), y estaría muy bien poder aplicarlo a todos los fotogramas.

En principio, ya que GIMP es un programa para edición de imágenes, no puedo pasarle un vídeo completo, tendré que pasarle fotogramas sueltos para que él los trate Lo que no quería yo era perder mucho tiempo, aunque si tengo que dejar el ordenador varias noches encendido no me importa. Por otro lado, tampoco me gustaría necesitar gran cantidad de disco, como experimento, convertí un vídeo de 3 minutos a imágenes fijas y ocupó más de 2Gb, no quiero saber lo que podría pasar con un vídeo de 1h de más resolución, por lo que tendremos que gestionar el uso de disco.

Por otro lado, y como dije antes, la velocidad es lo de menos, se puede tirar varios días si es necesario. No he utilizado técnicas multi-hilo, para procesar los fotogramas, y supongo que si necesito hacerlo con muchos vídeos, tarde o temprano tendré que hacer que se puedan ejecutar varias instancias de GIMP para procesar varios fotogramas a la vez, aunque también estaría bien evitar la carga del programa una y otra vez.

Para empezar, debemos tener unas nociones de Scheme, el lenguaje utilizado por las extensiones Script-fu de Gimp. Para empezar te recomiento este post.
En el script que vamos a hacer ahora, nos centraremos en abrir la imagen, obtener un drawable (algo así como un lienzo a partir de la imagen) y salvar dicho lienzo en un archivo. Para estas operaciones he optado por utilizar el formato PNG, aunque podríamos haber utilizado lossless jpeg, tiff, o cualquier otro formato que aplique compresión de imagen sin pérdidas. Es importante esto, porque la compresión de vídeo normalmente también tiene pérdida y siempre que extraemos y volvemos a grabar se producen, por lo tanto no queremos que en la aplicación del efecto ocurran más pérdidas por otro lado.

Script para GIMP

El script que guardaremos en .gimp-2.x/scripts/ se llamara gaussblursel.scm (donde 2.x es vuestra versión de GIMP), y contendrá lo siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(define (gaussblursel infile outfile radius max-delta)
    (let*
        (
            (image (car (file-png-load 1 infile infile)))
            (drawable (car (gimp-image-active-drawable image)))
        )
    (plug-in-sel-gauss 1 image drawable radius max-delta)

    (file-png-save 1 image drawable outfile outfile
          1 9 0 0 0 0 0)   
            ; 1 Adam7 interlacing?
            ;   0 deflate compression factor (0-9)
            ;     0 Write bKGD chunk?
            ;       0 Write gAMMA chunk?
            ;         0 Write oFFs chunk?
            ;           0 Write tIME chunk?    ?? backwards in DB Browser
            ;             0 Write pHYS chunk?  ?? backwards in DB Browser
    )
)

Aquí abrimos, procesamos el archivo y lo salvamos, utilizamos plug-in-sel-gauss que es la función que nos proporciona el desenfoque gaussiano selectivo y es el script que ejecutaremos para cada fotograma de vídeo.

Shell script

Ahora, crearemos un shell script que extraiga fotogramas del vídeo, los procese (con el script de GIMP), y los vuelva a meter en el vídeo. Para ello, extraeremos del vídeo intervalos de un segundo (suelen ser unos 25/30 fotogramas), lo que ocupa poco espacio en disco duro. Una vez ha extraído los fotogramas, llama varias veces a gimp en modo no-interactivo (desde consola, sin necesidad de abrir ventanas), procesa todos los fotogramas con el script anterior, los salva y los va metiendo todos en un vídeo llamado resultado.mp4; eso sí, para que no haya problema con el audio, cogeremos la misma pista de audio del vídeo original y la implantaremos en el vídeo destino.
Este script, para calcular la duración del archivo de vídeo y los fotogramas por segundo del mismo utiliza el script videoinfo.
Allá va la primera versión del script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

VIDEO="$1"
SECS=$(videoinfo seconds "$VIDEO")
FPS=$(videoinfo fps "$VIDEO")
TEMPNAME=$(tempfile)
rm "$TEMPNAME"

for ((i=0;i<$SECS;i++)); do
    echo "SEGUNDO: $i de $SECS" >&2
    avconv -i "$VIDEO" -an -q:v 1 -ss $i -t 1  -f image2 $TEMPNAME%05d.png
    for img in $TEMPNAME*.png; do
    gimp -i -b "(gaussblursel \"$img\" \"$img\" 8 40)" -b '(gimp-quit 0)'
    cat $img
    rm $img
    done
done | avconv -r $FPS -f image2pipe -c:v png -i pipe:0 -i "$VIDEO" -c:v h264 -c:a mp3 -map 0:v:0 -map 1:a:0 resultado.mp4

Al final del post, optimizaremos un poco y sólo un poco el script, para no cargar tantas veces gimp, aunque se cargará el programa al menos una vez por segundo.

Análisis del script

En principio, una vez extraídos SECS (segundos de duración) y FPS (fotogramas por sengundo), obtenemos un nombre de fichero temporal (para almacenar los fotogramas del segundo con el que trabajaremos cada vez.
En cada segundo que recorramos con el bucle for, extraeremos todos los fotogramas en archivos PNG cuyo nombre finalizará con números consecutivos. Una vez extraído ese segundo, proceso que suele durar poco (si el vídeo tiene los índices bien hechos). Con esta serie de ficheros (tantos como fotogramas por segundo tenga nuestro vídeo) llamaremos al script que hace el desenfoque y con cat, pondremos el contenido del archivo por la salida.
La salida combinada de todos los archivos procesados la cogerá avconv, cuya entrada es la pipe de salida del for, combinará el vídeo con el audio y listo. Podremos elegir los códecs que queramos. En mi caso, preferí hacerlo con h264 para vídeo y mp3 para el audio.

Primeros pasos

El primer paso para un buen resultado, es extraer varios fotogramas de muestra del vídeo y aplicar el efecto que queremos, para elegir los parámetros de radio de desenfoque y delta perfectos para todo el vídeo.
sentada_frame
A primera vista no apreciamos mucho, ya que lo que quería, sobre todo era quitar algo de granulado de los soportes y de las varias compresiones que lleva el vídeo (los vídeos tienen ya un tiempo y en aquellos comprimía los vídeos con gran pérdida, y en ocasiones cuando había que aplicar algún efecto, había que pasar por varios programas comprimiendo y descomprimiendo vídeo, por lo que el resultado siempre estaba algo más deteriorado.

Si ampliamos un área de imagen sí que podremos ver que se ha mejorado:
sentada_frame_g

Revisión del script

Esta es una versión que hice para que GIMP no se ejecutara tantísimas veces y es que la carga y descarga contínea del programa puede hacernos perder muchísimo rendimiento. En mi equipo, la ejecución del script tarda menos de la mitad con esta pequeña modificación:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

VIDEO="$1"
SECS=$(videoinfo seconds "$VIDEO")
FPS=$(videoinfo fps "$VIDEO")
TEMPNAME=$(tempfile)
rm "$TEMPNAME"

for ((i=0;i<$SECS;i++)); do
    echo "SEGUNDO: $i de $SECS" >&2
    avconv -i "$VIDEO" -an -q:v 1 -ss $i -t 1  -f image2 $TEMPNAME%05d.png
    SEQUENCE=()
    for img in $TEMPNAME*.png; do
    SEQUENCE+=(-b "(gaussblursel \"$img\" \"$img\" 2.50 12)")
    done

    gimp -i ${SEQUENCE[@]} -b '(gimp-quit 0)'

    for img in $TEMPNAME*.png; do
    cat $img
    rm $img
    done
done | avconv -r $FPS -f image2pipe -c:v png -i pipe:0 -i "$VIDEO" -c:v h264 -c:a mp3 -map 0:v:0 -map 1:a:0 resultado.mp4

En este caso, lo primero que hacemos es generar los argumentos del comando GIMP que ejecutaremos (será un script por cada fotograma, pero se los pasaremos todos juntos), una vez generado ejecutamos gimp y cuando terminemos, pondremos el contenido de los frames a la salida e iremos borrando archivos.

Listos para ejecutar

Una vez lo tengamos, todo listo, en el script editamos los parámetros del efecto (en la línea de llamada a gimp) y ejecutamos. Tardará un tiempo, pero obtendremos nuestro resultado.
Al final, obtenemos un vídeo que se ve un poco mejor que el original, tampoco se pueden hacer milagros, pero las texturas son un poco más suaves (el vídeo es muy antiguo):

Ideas para el futuro

Como idea, lo que podríamos hacer será generar varias secuencias de archivos y ejecutar varias instancias de gimp con scripts diferentes. Si tienes un equipo con varios núcleos, podrás aprovecharlos todos para procesar fotogramas de vídeo y terminar mucho antes, utilizando herramientas de bash para ejecución en segundo plano, o herramientas propias para ejecutar tareas concurrentemente en shell scripts.

Foto principal: Sergey Turkin

The post Cómo aplicar filtros de GIMP a un vídeo appeared first on Poesía Binaria.

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

Variable not found

Enlaces interesantes 250

July 18, 2016 06:46 AM

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

.NET

ASP.NET

.NET Core / 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...

Koalite

Patrones de Reutilización de Código entre Componentes de ReactJS

July 18, 2016 05:06 AM

Reconozco que cuando empecé a jugar con ReactJS una de las cosas que menos me gustó fue el uso de JSX. Mezclar pseudo-HTML con el código Javascript me parecía sucio y tener que extender el lenguaje para ello, aun más. Reconozco también que, en aquel momento, se me pasó por alto la que quizá sea la característica más importante de JSX: que se trata de objetos Javacript, ni más, ni menos.

Tiene su gracia, sobre todo porque un año antes cuando jugaba con Hiccup para generar HTML desde Clojure ya decía esto:

Aquí se aprecia una de las ventajas de usar el mismo lenguaje para las plantillas que en el resto de la aplicación: podemos usar cualquier construcción del lenguaje dentro de las plantillas.

Poder aprovechar toda la potencia del lenguaje “host” para generar plantillas permite muchas alternativas a la hora de estructurar nuestro código para hacerlo más legible y reutilizable.

En este post vamos a ver algunas de las formas más habituales de conseguir esto en ReactJS a través de ejemplos muy sencillos.

OJO: Como siempre, la mejor opción es aislar al máximo la lógica de tu aplicación de las librerías que estés utilizando. Si puedes extraer la lógica que quieres reutilizar a funciones, clases o módulos independientes de ReactJS, esa es la mejor salida. Las técnicas que vamos a ver en este post están pensadas para escenarios en los que necesariamente tienes que estar tratando con componentes de ReactJS.

Crear componentes específicos

Cualquier persona que haya leído algo sobre ReactJS conoce esta idea. Si tengo varios componentes que forman una unidad “lógica”, puedo extraerlos a su propio componente para que quede más claro cuál es su misión y de qué forma se utilizan.

Si tengo un componente en el que parte de su render se dedica a mostrar una cabecera:

class MyComponent extends React.Component {
  render() {
    return <div>
             <header>
               <h1>Resultados</h1>
               <img src='/img/search-results.png'/>
             </header>           
             <div>...</div>
           </div>;
  }
}

Puedo extraer la construcción de la cabecera a su propio componente para abstraer los detalles y facilitar la legibilidad y la reutilización:

class Header extends React.Component {
  render() {
    return <header>
             <h1>{this.props.text}</h1>
             <img src={this.props.icon}/>
           </header>;
  }
}

class MyComponent extends React.Component {
  render() {
    return <div>
             <Header text='Resultados' icon='/img/search-results.png'/>
             <div>...</div>
           </div>;
  }
}

Cuando se trata de componentes sin estado, stateless components, podemos implementarlos directamente como una función que recibe un objecto props y devuelve el JSX correspondiente:

const Header = ({text, icon}) => <header>
  <h1>{text}</h1>
  <img src={icon}/>
</header>

Esta es la forma más básica de reutilizar código y la verdad es que no es muy espectacular. Cualquier librería medio decente permite hacer cosas similares (aunque probablemente no tan limpias como se hace con los componentes sin estado).

Generar componentes desde funciones

Puesto que los componentes de ReactJS son objetos normales y corrientes, podemos utilizar funciones formales y corrientes para construirlos. El caso más típico, que encontramos en casi todos los tutoriales de ReactJS, es utilizar un map para construir una colección de componentes:

render() {
  const contacts = this.props.contacts
    .map(c => <Contact name={c.name} email={c.email}/>);

  return <Contacts>{contacts}</Contacts>;
}

Visto así, no parece gran cosa porque cualquier sistema de plantillas tiene un for, repeat o similar para aplicar una plantilla a una colección de objetos, pero en ReactJS podemos hacer cosas más interesantes.

Por ejemplo, podemos fijar parte de los parámetros de un componente para evitar repetirnos y facilitar su uso:

// Simplificamos el uso del componente Header del ejemplo anterior
// para no tener que indicar la ruta del icono y homogeneizar los
// textos de los títulos
function title(text, icon) {
  text = 'My App: ' + text;
  icon = icon ? '/img/' + icon + '.png' || '/img/default.png';
  return <Header text={text} icon={icon}/>
}

const header = title('Resultados', 'search-results');

También podemos crear fácilmente factorías de componentes:

function createChart(type, title, data) {
  switch (type) {
    case 'bar': return <BarChart title={title} data={data}/>
    case 'pie': return <PieChart title={title} data={data}/>
    case 'line': return <LineChart title={title} data={data}/>
  }
}

// Cambiamos el tipo de gráfico dependiendo del estado actual
// del componente
const chart = createChart(this.state.chartType, title, data);
return <div>
  <h2>Some Chart</h2>
  {chart}
</div>;

Con esta técnica se puede resolver cualquier problema que te puedas encontrar, aunque hay ocasiones en que el resultado no es el más cómodo o legible, por lo que no está de más conocer las siguientes técnicas.

Encapsular componentes en otros componentes

A veces necesitas un componente cuya misión es servir como contenedor de otros componentes. Un caso habitual es un componente que actúa como panel, pestaña, tarjeta o algún concepto similar. Aunque puedes hacerlo usando funciones, es más sencillo aprovechar la sintaxis de JSX para hacerlo:

class Card extends React.Component {
  render() {
    <div className='card-component'>
      <div className='close-button'>×</div>
      
      {this.props.children}
    </div>
  }
}

const contactCard = <Card>
                      <div>Name: {this.props.name}</div>
                      <div>Email: {this.props.email}</div>
                    </Card

Una cosa a tener en cuenta es que no tenemos que limitarnos a componentes que añadan elementos HTML, y podemos tener componentes que encapsulan otro componente para añadirle algún tipo de funcionalidad.

Por ejemplo, podemos tener un componente que se encargue de proporcionar datos al componente que encapsula:

class Loader extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true,
      data: null
    }
  }
  componentDidMount() {
    this.props.load().then(data => {
      this.setState({
        loading: false,
        data
      });
    });
  }
  render() {
  
    if (this.state.loading) {
      return <LoadIndicator />;
    }
  
    let child = React.Children.only(this.props.children); 
    child = React.cloneElement(child, {data: this.state.data});
    return {child};
  }
}

// Para usarlo, encapsulamos el componente que necesita los datos,
// en este caso CustomerList dentro de un Loader, y éste se
// encargará de pasarle los datos a través del objeto props
<Loader load={loadCustomers}>
  <CustomerList/>
</Loader>

Herencia entre componentes

Es una de las alternativas que menos me gustan, en parte porque la herencia supone un acoplamiento muy fuerte, pero sobre todo porque Javascript no está pensado para este tipo de herencia y podemos acabar en casos un poco extraños. Aun así, tiene su utilidad y permite resolver algunos de los escenarios que antes resolvíamos con mixins.

Por ejemplo, si tenemos componentes que pueden reaccionar a timers podríamos crear una clase base encargada de gestionar los timers en el momento de desmontar el componente:

class TimedComponent extends React.Component {

  constructor() {
    this.timeouts = [];
    this.intervals = [];
  }
  
  setTimeout(callback, delay) {
    this.timeouts.push(window.setTimeout(callback, delay));
  }
  
  setInverval(callback, delay) {
    this.intervals.push(window.setInterval(callback, delay));
  }
  
  componentWillUnmount() {
    this.intervals.forEach(window.clearInterval);
    this.timeouts.forEach(window.clearTimeout);
  }
}

// Componente que necesita usar timers 
class Timeline extends TimedComponent {
  componentDidMount() {
    this.setInterval(this.refreshTweets, 30 * 1000);
  }
  refreshTweets() {
    ...
  }
}

Por desgracia, la mayoría de los casos que he visto son parecidos al ejemplo que acabo de poner, donde se utiliza la clase base para añadir métodos de utilidad, lo que es algo cuestionable si entiendes la herencia como una relación de “es un”.

Además tiene la clara limitación de que sólo podemos heredar de una clase, por lo que este mecanismo no escala demasiado bien (aunque tiene sus usos).

Componentes de orden superior

Los componentes de orden superior son otra forma de reutilizar lógica entre componentes de ReactJS. También nos permite resolver algunos de los escenarios que resolvíamos con mixins, y se basan en la idea de que las clases en Javascript no son más que funciones constructores.

Puesto que una clase es una función, y en Javascript las funciones pueden usarse como parámetros y valores de retorno de otras funciones, podemos crear clases al vuelo a partir de otras clases.

Hace poco escribí una explicación más detallada de cómo funcionan los componentes de orden superior en ReactJS, en la que mostraba este ejemplo:

function addLabel(Control) {
  return class extends ReactJS.Component {
    render() {
      return <label>
               {this.props.label}
               <Control {...this.props}/>
             </label>;
    }
  }
}

const LabeledTextBox = addLabel(TextBox);
const LabeledDateTime = addLabel(DateTimePicker);

<LabeledTextBox label='Nombre:' onChange={this.updateName}/>
<LabeledDateTime label='Fecha:' onChange={this.updateDate}/>

La función addLabel se encarga de recibir un componente y crear una nueva clase de componentes que encapsula ese componente añadiéndole un label. Así, a partir de componentes que ya tuviéramos creados, como un TextBox o un DateTimePicker podemos generar nuevos tipos de componentes.

Una ventana de este método frente al de herencia es que podemos componer varias funciones para crear nuestra nueva clase de componentes, por lo que no estamos limitados a añadir una única funcionalidad. Por ejemplo, podríamos tener funciones para añadir setInterval y para hacer la carga de datos, y usarlos así:

const addSetInterval = function(Component) {...};
const addDataLoader = function(Component, loadData) {...};

const MyEnhancedComponent = addSetInterval(addDataLoader(MyComponent));

Resumen

El hecho de que ReactJS utilice Javascript para todo, incluida la generación del HTML, en lugar de emplear un sistema de templates basado en strings como hacen otras librerías, permite aprovechar todas las características de Javascript para organizar el código de nuestra capa de presentación.

En este post hemos visto unas cuantas formas en que podemos abstraer fragmentos de funcionalidad para hacer nuestro código más legible o reutilizable. Cada una de ellas ofrece ciertas ventajas, por lo que es habitual mezclarlas todas dentro de una misma aplicación en función de cada escenario.

Aun así, no hay que olvidar que la mejor forma de reutilizar código cuando usamos cualquier librería es haciendo ese código independiente de ella, por lo que antes de plantearte de qué forma puedes crear componentes de ReactJS para reutilizar código, piensa si es posible hacerlo con funciones u objetos de Javascript completamente desacoplados de ReactJS.

Posts relacionados:

  1. Componentes de Orden Superior (Higher Order Components) en ReactJS
  2. Crear un componente con ReactJS
  3. Un mixin para carga asíncrona de datos en ReactJS

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

Blog Bitix

Cómo crear un proxy inverso entre el servidor web Nginx y un servidor de aplicaciones Java

July 16, 2016 11:30 AM

Continuando la serie de artículos sobre varios aspectos realizados muy comúnmente en las aplicaciones y servidores web en este artículo explicaré como hacer que un servidor web nginx haga de proxy inverso para un servidor de aplicaciones Java en este caso Tomcat.

Nginx
Tomcat

Las aplicaciones web dinámicas Java se despliegan en un contenedor de servlets o un servidor de aplicaciones como WildFly que implementa las especificaciones de los estándares de Java EE pero es habitual que los usuarios no accedan directamente al contenedor de aplicaciones Java sino que se ponga delante un servidor web como Apache o Nginx con la tarea de que realice algunas tareas. Las tareas que puede realizar un servidor web son varias como:

Un proxy inverso recibe las peticiones de internet y las reenvía a los servidores de una red interna sin necesidad de que los clientes conozcan la red interna

Para que un servidor web como Nginx actúe como proxy inverso o reverse proxy para un servidor de aplicaciones debemos añadir unas pocas directivas al archivo de configuración del servidor web. En el caso de Nginx usando la directiva proxy_pass donde indicamos para una localización la URL del servidor de aplicaciones a la que se le solicitará el contenido, en el ejemplo usando un servidor Tomcat.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/6dd14f54a22db38e07e9935189461a27/raw/nginx.conf">nginx.conf</pre></a></noscript>

Una forma fácil de probarlo es usando Docker y Docker Compose que en varios artículos introductorios siendo el primero el inicio básico de Docker comento como empezar a usarlo y en que consiste esta nueva forma de ejecución para las aplicaciones. Con el siguiente archivo de Docker Compose creamos dos contenedores uno para Nginx en el que proporcionamos su configuración personalizada y otro contenedor para Tomcat.

<noscript><pre><a href="https://gist.githubusercontent.com/picodotdev/6dd14f54a22db38e07e9935189461a27/raw/docker-compose.yml">docker-compose.yml</pre></a></noscript>

Al hacer un proxy inverso debemos tener en cuenta que el servidor Tomcat si devuelve URL absolutas las haga siendo las del servidor web por las que se accede a la aplicación, si únicamente genera URL relativas no deberemos hacer nada pero en el caso de absolutas el servidor Tomcat deberá conocer el protocolo usado en el servidor web (HTTP o HTTPS) y el puerto del servidor web que suele ser 80 para HTTP y 443 para HTTPS pero que en el servidor Tomcat suele ser 8080 para HTTP y 8443 para HTTPS. Si el protocolo y puerto usado en el servidor web y servidor de aplicaciones es diferente y una aplicación genera URL absolutas el servidor de aplicaciones deberá tener esto en cuenta que es lo que se usa el en servidor web.

En la documentación se comentan varios parámetros de configuración de Tomcat como proxyPort y scheme que ajustan la información devuelta por los métodos request.getServerPort() y request.getScheme() y que nos servirá en caso de tener que generar URLs absolutas.

Arrancado los contenedores con el comando docker-compose up accediendo al servidor web veremos que el contenido proporcionado es el ofrecido por Tomcat, que con la configuración del ejemplo es la página de inicio de Tomcat. En las cabeceras de respuesta Nginx añade una, Server, indicando su versión.

Nginx configurado como proxy inverso de un servidor de aplicaciones Tomcat

En la documentación sobre reverse proxy de Nginx se explican algunas directivas más para pasar al servidor Tomcat la dirección IP del usuario usando cabeceras HTTP, en la configuración de Nginx usando proxy_set_header.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando docker-compose up.

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

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

Marketing | Prospecting tools

July 15, 2016 11:57 PM

The Big List: 40 Prospecting Tools for the Modern Salesperson http://flip.it/3uhUL

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

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

Ultra Compressor II

July 15, 2016 10:56 AM

Hace unos meses hablando de AIN, os adelantaba mi interés en hacer exactamente lo mismo, pero con otra joya de la compresión bajo DOS: Ultra Compressor II. Creado por AIP-NL (Ad Infinitum Programs), y bautizado comercialmente como Ultra Compressor II Pro, no dejaba de ser un desarrollo de tres individuos: Nico de Vries, Danny Bezemer [...]

» 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