• Shortcuts : 'n' next unread feed - 'p' previous unread feed • Styles : 1 2

» Publishers, Monetize your RSS feeds with FeedShow:  More infos  (Show/Hide Ads)


Date: Friday, 29 Aug 2014 22:16
Apache Tapestry

Hace un poco más de una año publiqué el libro PlugIn Tapestry que como su nombre ya adelanta versa sobre el framework Apache Tapestry para el desarrollo de aplicaciones y páginas web con Java. Casi 300 páginas de documentación que describen este framework, como usarlo y va más allá explicando como realizar algunas de las tareas comunes que es necesario hacer en casi todas las aplicaciones web independientemente del framework que elijamos pero en este caso visto desde el punto de vista de Tapestry.

En esta actualización he revisado capítulos como el Inicio rápido para empezar a desarrollar en unos pocos minutos, la sección que trata sobre Plantillas para dar a las páginas un aspecto común, la sección que trata sobre unas Convenciones para los archivos de literales, ampliada la sección de Principios en la que se detalla cuales son las ideas y objetivos por los que se rige el framework, también reescrita la sección de Integración con Spring, una de las librerías más usadas y que facilita el desarrollo enormemente.

Pero además he incluido nuevo contenido que he publicado en diferentes artículos en mi bitácora desde la plublicación original del libro. Estos han sido Como ejecutar Apache Tapestry en un servidor de aplicaciones JBoss o Wildfly, explicada la página Dashboard que nos permite obtener información interesante mientras desarrollamos como que páginas y componentes incluye la aplicación o las estadísticas de Hibernate, explicadas las diferencias del modelo «pull» en el que se basa Tapestry del modelo «push» en el que se basan la mayoría de frameworks orientados a acciones independientemente del lenguaje (Grails, Django, Symfony, ASP.NET MVC), como servir los recursos estáticos desde una red de contenidos (CDN, Content Delivery Network), la anotación Cached que permite evitar invocaciones a métodos devolviendo el resultado obtenido en la primera invocación, como usar Tapestry en una aplicación de forma «standalone» fuera del contexto de un servidor de aplicaciones, la anotación Secure y como añadir más seguridad usando el protocolo seguro HTTPS y finalmente añadido un nuevo formato en el que se puede leer el libro, HTML, además de PDF y los formatos para libro electrónico EPUB y MOBI.

¡Y luego compártelo!

Author: "--"
Send by mail Print  Save  Delicious 
Date: Friday, 29 Aug 2014 11:04
Apache Tapestry

Por motivos de usabiliad en algunas páginas se incluyen dos barra de botones, una antes de una larga sección y otra después. El motivo de la doble barra de botones inicial es que no sea necesario hacer «scroll» hasta el final de la página para acceder a los botones y realizar la acción que permitan. Por el contrario, inlcuir la barra de botones al final de la página permite que una vez seleccionados los elementos o revisado el contenido de la sección hacer disponibles las acciones que es probable que se quieran realizar.

Dependiendo del framework web que utilicemos podremos hacerlo de una o varias formas pero de lo que estamos seguros es que copiar y pegar haciendo que el código esté duplicado no es una buena idea por los problemas de mantenimiento que puede suponer. Pero crear una pequeña plantilla o archivo exclusivo para incluir la barra de botones tampoco es la solución ideal, ¿por que? pues porque creando un archivo específico con la botonera puede que nos ocasione un problema que denominaré de «microgestión», es decir, nos obliga a crear un montón de pequeños archivos pequeñitos y hacer referencia o utilizar el mecanismo de inclusión que dispongamos para usar el contenido en unos de otros. En una aplicación grande esta microgestión si nos vemos obligados a ella puede llegar a ser molesta al desarrollar cuanto menos.

¿Como se puede evitar? En el framework Apache Tapestry la doble botonera puede hacerse de varias formas una de ellas es crear un componente pero esto nos obliga a crear un archivo para la clase java y probablemente tambien un archivo de plantilla con el contenido html causando el problema de la microgestión. Pero en Tapestry también podemos hacer uso del componente block que sirve para incluir en él cierto contenido y el componente delegate que sirve para emitir el contenido entre otras cosas de un componente block. Usando estos dos componentes podemos evitar la microgestión definiendo todo en un mismo archivo, además al tener todo en un mismo archivo el código será más fácilmente legible, quedándonos en un ejemplo de la siguiente forma:

<noscript><pre><code>&lt;!DOCTYPE html&gt; &lt;html t:type=&quot;layout&quot; titulo=&quot;Administración de productos&quot; xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_3.xsd&quot; xmlns:p=&quot;tapestry:parameter&quot;&gt; ... &lt;t:block id=&quot;edicionBlock&quot;&gt; &lt;t:remove&gt; En otros frameworks la lógica para obtener el título del bloque según se trate de un alta o una modificación, probablemente se hiciese metiendo lógica en la plantilla de presentación, dado que Tapestry permite llamar a métodos de la clase Java asociada al componente es mejor dejar esa lógica en el código Java de esta manera la plantilla será más sencilla y clara además de aprovecharnos del compilador. labels es un método definido en la página admin.producto que devuelve un mapa. &lt;/t:remove&gt; &lt;h1&gt;${labels.get('titulo')}&lt;/h1&gt; &lt;t:form t:id=&quot;form&quot; context=&quot;producto.id&quot; validate=&quot;producto&quot; clientValidation=&quot;none&quot; class=&quot;well&quot; role=&quot;form&quot;&gt; &lt;t:errors class=&quot;literal:alert alert-danger&quot; /&gt; &lt;t:delegate to=&quot;botonesEdicionBlock&quot;/&gt; &lt;div style=&quot;margin-top: 10px;&quot;&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;t:label for=&quot;nombre&quot; /&gt; &lt;div class=&quot;controls&quot;&gt; &lt;input t:type=&quot;textfield&quot; t:id=&quot;nombre&quot; value=&quot;producto.nombre&quot; size=&quot;100&quot; label=&quot;Nombre&quot; /&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;t:label for=&quot;descripcion&quot; /&gt; &lt;div class=&quot;controls&quot;&gt; &lt;input t:type=&quot;textarea&quot; t:id=&quot;descripcion&quot; value=&quot;producto.descripcion&quot; label=&quot;Descripción&quot; /&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;t:label for=&quot;cantidad&quot; /&gt; &lt;div class=&quot;controls&quot;&gt; &lt;input t:type=&quot;textfield&quot; t:id=&quot;cantidad&quot; value=&quot;producto.cantidad&quot; size=&quot;4&quot; label=&quot;Cantidad&quot; class=&quot;numeric&quot;/&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;form-group&quot;&gt; &lt;t:label for=&quot;fecha&quot; /&gt; &lt;div class=&quot;controls&quot;&gt; &lt;div class=&quot;input-group&quot;&gt; &lt;input t:type=&quot;textfield&quot; t:id=&quot;fecha&quot; type=&quot;date&quot; value=&quot;producto.fecha&quot; label=&quot;Fecha&quot; /&gt; &lt;span class=&quot;input-group-addon&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-calendar&quot;&gt;&lt;/span&gt;&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;t:delegate to=&quot;botonesEdicionBlock&quot;/&gt; &lt;/t:form&gt; &lt;/t:block&gt; &lt;t:block id=&quot;botonesEdicionBlock&quot;&gt; &lt;div class=&quot;btn-toolbar&quot;&gt; &lt;input t:type=&quot;submit&quot; class=&quot;btn btn-primary&quot; value=&quot;prop:labels.get('guardar')&quot; role=&quot;button&quot;/&gt; &lt;t:if test=&quot;producto.id&quot;&gt; &lt;a t:type=&quot;eventlink&quot; event=&quot;eliminar&quot; context=&quot;producto.id&quot; role=&quot;button&quot; class=&quot;btn btn-danger&quot; style=&quot;color: white;&quot;&gt;Eliminar&lt;/a&gt; &lt;/t:if&gt; &lt;input t:type=&quot;submit&quot; class=&quot;btn btn-danger&quot; value=&quot;Cancelar&quot; mode=&quot;cancel&quot; role=&quot;button&quot;/&gt; &lt;/div&gt; &lt;/t:block&gt; &lt;/html&gt;</code></pre></noscript>

En Grails por poner un ejemplo de un framework que no usa el concepto de componentes la forma habitual de hacerlo es usando un g:include y con ello teniendo microgestión. Pero retorciendo un poco (creo) en este caso el uso de Grails podemos emplear la etiqueta g:set para establecer el contenido de la botonera y emitir su contenido dos veces en el gsp.

<noscript><pre><code>... &lt;g:set var=&quot;botonesEdicionBlock&quot;&gt; &lt;div class=&quot;btn-toolbar&quot;&gt; &lt;input type=&quot;submit&quot; class=&quot;btn btn-primary&quot; value=&quot;${message(code: 'guardar')}&quot; role=&quot;button&quot;/&gt; &lt;g:if test=&quot;${producto.id}&quot;&gt; &lt;a action=&quot;eliminar&quot; params=&quot;${[id: producto.id]}&quot; role=&quot;button&quot; class=&quot;btn btn-danger&quot; style=&quot;color: white;&quot;&gt;Eliminar&lt;/a&gt; &lt;/t:if&gt; &lt;input type=&quot;submit&quot; class=&quot;btn btn-danger&quot; value=&quot;{message(code: 'guardar')}&quot; role=&quot;button&quot;/&gt; &lt;/div&gt; &lt;/g:set&gt; &lt;g:form ...&gt; ... ${botonesEdicionBlock} &lt;div style=&quot;margin-top: 10px;&quot;&gt; ... &lt;/div&gt; ${botonesEdicionBlock} &lt;/t:form&gt;</code></pre></noscript>

El código completo de este ejemplo del caso de Tapestry está en un repositorio de GitHub. Si estás interesado en conocer más en profundidad como funciona Tapestry y sus múltiples «killer features», bastantes mucho más importantes que lo explicado en este artículo, puedes descargarte el libro PlugIn Tapestry que he escrito, de forma gratuita, sin registros, y en varios formatos ¿que más puedes pedir?. Y si te interesa el tema puedes suscribirte al canal RSS de esta bitácora para no perderte nada del nuevo contenido que publique, no solo sobre Tapestry, sino también sobre Java, Linux, …

Referencia:
Libro PlugIn Tapestry
Más artículos sobre Apache Tapestry

Author: "--"
Send by mail Print  Save  Delicious 
Date: Wednesday, 27 Aug 2014 16:00
TMNT

TMNT

Este mes se estrenó una nueva película de las amadas Tortugas Ninja. Fue dirigida por Jonathan Liebesman (si no les suena conocido no se sientan mal), y producida por la empresa de Michael Bay (conocido por destruir franquicias de nuestra infancia).

Con el amigo Hank Scorpio de Multiverseros la vimos en el cine. Aprovechando que fuimos de los pocos en hacer el sacrificio y soportar toda la película, escribimos una reseña para la columna La Reseña Conversada en el sitio de Multiverseros. Pueden leer la nota acá:
TMNT: La Reseña Conversada

Por si mis sutiles comentarios sobre los encargados no les han dado una pista todavía, les comento que la película no me gustó para nada. Vean más al respecto en TMNT: La Reseña Conversada.

Si quieren seguir a nuestras queridas Tortugas Ninja en algún medio actual, les recomiendo la serie de animación en Nickelodeon o alguna de los tantos títulos publicados en cómic. Pueden leer un buen repaso de la historia de las Tortugas Ninja en sus varias interpretaciones, en el artículo Hank y su cajón de juguetes: Las Tortugas Ninja.

Author: "--"
Send by mail Print  Save  Delicious 
Date: Monday, 25 Aug 2014 23:23

Los días post-fiestas de pueblo suelen hacerse cuesta arriba, y la semana pasada no fue una excepción, el lunes fue relajado pero el resto de la semana hubo que ponerse las pilas.

Estuvimos analizando el alcance de un proyecto web de gestión de contenidos y una pequeña parte de gestión documental, para hacer una propuesta tanto para el desarrollo de la parte backend como la de frontend.

Los proyectos actuales:

  • Pequeños cambios en minchador, básicamente mejorar el mailing entre restaurante y comensal.
  • Resolver errores de copy en mhop y alguna que otra tarea de mantenimiento.
  • De nuevo fue Nort lo que se llevó el grueso de la dedicación, estuve implementando varias historias de usuario relacionadas con edición de perfiles, tanto para la versión web como la móvil.
  • Empezar con la web de Biera Solutions, estuve trabajando en el contenido, que siempre es algo que se me hace difícil.

Buena semana

Author: "--"
Send by mail Print  Save  Delicious 
Date: Monday, 25 Aug 2014 05:06

Dentro del caótico vibrante mundo de las librerías para Javascript, existen muchas alternativas para mostrar gráficos en pantalla, pero la que más me gusta, por su equilibro entre facilidad de uso y vistosidad de resultados es chart.js.

Micro introducción a Chart.js

En la completa documentación de chart.js podéis encontrar información de sobra para manejarla con soltura, pero para que os hagáis una idea, para mostrar un gráfico de tarta típico sólo hace falta el siguiente código (podéis jugar con él en jsfiddle):

<canvas id="sales-chart" width="400" height="400"></canvas>
var context = document.getElementById('sales-chart').getContext('2d');
var chart = new Chart(context).Pie([
  { label: 'Red', value: 75, color: '#F7464A' },
  { label: 'Not-Red', value: 23, color: '#4D5360' }
]);

Lo único que necesitamos es tener un elemento canvas (como el que usamos para implementar el juego de la vida en Javascript) y crear un objeto Chart sobre él. Este objeto cuenta con distintos métodos para crear cada tipo de gráfico, y cada método recibe dos objetos, uno obligatorio con los datos a mostrar, y otro opcional con la configuración del gráfico (ejes, leyendas, etc.).

Disclaimer

Lo lógico sería haber escrito este post explicando cómo usar Chart.js en una directiva de angularjs que para eso es el framework de moda (con permiso de ReactJS), pero como mi relación con angular está de capa caída y la aplicación donde quería integrarlo usa Knockout.js, ha tocado Knockout.js.

En ningún momento pretendo incentivar el uso de Knockout.js ;-)

Creando un custom binding para Knockout.js

Ya expliqué en su momento cómo crear custom bindings con Knockout.js, así que no me extenderé mucho, pero la idea básica es que podemos añadir nuevas propiedades al objeto ko.bindingHandlers y a través de las funciones init y update indicar cómo debe comportarse nuestro binding.

La estructura es la siguiente:

ko.bindingHandlers.myCustomBinding = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    // Función invocada cuando se crea el binding. 
    // Úsala para modificar el DOM, enganchar manejadores de eventos, etc.
  },
  update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
    // Función invocada cada vez que cambia el valor de la propiedad enlazada
    // Úsala para actualizar la información mostrada en el DOM
  }
};

Viendo esto, definir un custom binding no parece muy complicado. Realmente lo más difícil en este caso es decidir cómo queremos construir el API para definir nuestro binding.

Chart.js permite configurar infinidad de parámetros tanto a través de los datos que se le pasan, como a través de las opciones del gráfico, pero para que sea relativamente cómodo de manejar, he optado por crear la siguiente sintaxis:

<canvas width="400" height="400" 
  data-bind="chartType: 'Pie', chartData: quarterSales, chartOptions: salesFormat">
</canvas>

La idea es definir no uno, sino tres bindings para indicar por separado el tipo de gráfico, los datos que queremos mostrar y las opciones del gráfico. Así podremos enlazarlas por separado y, por ejemplo, permitir al usuario mostrar la misma información como gráfico de tarta o de barras, o actualizar la información en tiempo real según se reciben nuevos datos.

El código para implementar esto es el siguiente:

(function(ko, Chart) {

  ko.bindingHandlers.chartType = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
      if (!allBindings.has('chartData')) {
        throw Error('chartType must be used in conjunction with chartData and (optionally) chartOptions');
      }
    },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
      var ctx = element.getContext('2d'),
        type = ko.unwrap(valueAccessor()),
        data = ko.unwrap(allBindings.get('chartData')),
        options = ko.unwrap(allBindings.get('chartOptions')) || {},
        chart = new Chart(ctx);

      chart[type](data, options);
    }
  };

  ko.bindingHandlers.chartData = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
      if (!allBindings.has('chartType')) {
        throw Error('chartData must be used in conjunction with chartType and (optionally) chartOptions');
      }
    }
  };

  ko.bindingHandlers.chartOptions = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
      if (!allBindings.has('chartData') || !allBindings.has('chartType')) {
        throw Error('chartOptions must be used in conjunction with chartType and chartData');
      }
    }
  };

})(ko, Chart);

En realidad, todo el proceso se hace a en el binding chartType, desde el cual podemos acceder a los valores enlazados en chartData y chartOptions a través del objeto allBindings. Los otros dos bindings se limitan a validar que la configuración de bindings es correcta para hacer la cosa un poco más amigable para el usuario.

El código es bastante sencillo, aunque tener que usar ko.unwrap para tratar con los observables de Knockout.js lo ensucia un poco (nadie dijo que Knockout.js fuese bonito). Al final todo se reduce a obtener los valores enlazadas e invocar “dinámicamente” el método del objeto Chart correspondiente al valor enlazado como chartType.

Podéis verlo en funcionamiento (y jugar con él) en este jsfiddle:

El código es tan simple que podéis copiarlo del propio post, pero si lo queréis un poco más limpio, aquí tenéis el código fuente completo.

Una nota final

Hay que tener en cuenta que en el diseño del binding ha primado la comodidad de uso frente a la eficiencia. Tal y como está diseñado, cada vez que se modifica algo del gráfico, éste se redibuja por completo, lo que puede ser un problema si estamos actualizándolo en tiempo real. Chart.js expone métodos optimizados para ese caso de uso y, si te enfrentas a ese escenario, sería bueno echarles un vistazo.

Posts relacionados:

  1. Tutorial jQuery Mobile + Knockout (III): Definiendo el ViewModel con Knockout
  2. Tutorial jQuery Mobile + Knockout (I): Sentando las bases
  3. Tutorial jQuery Mobile + Knockout (y IV): Configurando el Data Binding
Author: "--"
Send by mail Print  Save  Delicious 
Date: Friday, 22 Aug 2014 09:09
Apache Tapestry

El protocolo seguro https hace que los datos que viajan entre el servidor y el cliente a través de internet estén cifrados de modo que nadie más pueda saber cual es es la información intercambiada ni se pueda alterar sin el conocimiento entre las dos partes. Estas propiedades nos son de interés para ciertas partes de una aplicación o en algunos casos la aplicación entera. ¿Cuales son estos casos? Son aquellos en los que queramos garantizar una mayor seguridad, estos pueden ser para proteger usuarios y contraseñas de autenticación para iniciar sesión, ciertos datos sensibles como datos personales, datos de tarjetas de crédito, … evitando que una tercera parte los obtenga y los utilice para su provecho propio y supongan un problema de seguridad en la aplicación.

Es casi obligatorio forzar a que ciertas páginas de una aplicación o página web funcionen mediante el protocolo seguro https como las páginas de inicio de sesión donde los usuarios se autentican normalmente introduciendo su usuario y contraseña, páginas de compra donde los usuarios introducen los datos de su tarjeta de crédito o algunas secciones de una aplicación como las secciones de las cuentas de los usuarios o un backoffice.

En Apache Tapestry hay varias formas de forzar a que una determinada página use el protocolo seguro de modo que si se accede por el protocolo no seguro http la aplicación obligue a usar https haciendo una redirección. Una de ellas es utilizar la anotación @Secure en las páginas que queramos obligar a usar https. Basta con anotar las clases de las páginas con @Secure y Tapestry automáticamente hará la redirección al protocolo https cuando se acceda con http a la página.

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.pages; ... @Secure public class Login { ... } </code></pre></noscript>

Probablemente nos interese configurar el puerto y el host que usará Tapestry al hacer la redirección para que coincidan con el usado en el servidor al que accede el usuario, sobre todo si en la aplicación usamos un servidor web proxy como apache, lighttpd o nginx delante del servidor de aplicaciones donde realmente se ejecuta la aplicación web. El puerto seguro del protocolo https predeterminado es 443 pero en el servidor de aplicaciones tomcat por defecto es 8443. Esto en tapestry lo indicamos configurando con ciertos símbolos.

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services; ... public class AppModule { public static void contributeApplicationDefaults(MappedConfiguration&lt;String, Object&gt; configuration) { ... configuration.add(SymbolConstants.SECURE_ENABLED, true); configuration.add(SymbolConstants.HOSTPORT, 8080); configuration.add(SymbolConstants.HOSTPORT_SECURE, 8443); ... } ... }</code></pre></noscript>

Para probar mientras desarrollamos, al menos en nuestro equipo, que la redirección se hace correctamente empleando el plugin de gradle para tomcat podemos hacer que el servidor de desarrollo se inicie con el puerto https disponible. Para usar https se necesita un certificado digital que el plugin de gradle para tomcat se encarga de generar al iniciar la aplicación, aunque sea autofirmado y el navegador alerte que no lo reconoce como firmado un una autoridad en la que confíe, si lo aceptamos podemos acceder a la aplicación sin más problema. Usando gradle la configuración que podemos emplear es:

<noscript><pre><code>... buildscript { repositories { mavenCentral() jcenter() } dependencies { classpath 'org.gradle.api.plugins:gradle-tomcat-plugin:1.2.4' } } ... tomcat { httpPort = 8080 httpsPort = 8443 enableSSL = true } ...</code></pre></noscript>

La anotación @Secure en Tapestry es suficiente pero podemos hacer lo mismo empleando Shiro. Integrando Shiro con Tapestry nos permite realizar autenticación y autorización, pero además empleando Shiro también podemos obligar a usar el protocolo https del mismo modo que lo hacemos con la anotación Secure. Cualquiera de las dos formas es perfectamente válida y depende más de cual prefiramos. Con la anotación @Secure deberemos anotar cada página, con Shiro podemos tener centralizado en un único punto en que páginas requerimos https. Con Shiro la configuración se hace con una contribución al servicio SecurityConfiguration y usando el método contributeSecurityConfiguration del módulo y la clase SecurityFilterChainFactory y su método ssl(). Un ejemplo es el siguiente:

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services; ... public class AppModule { .... public static void contributeSecurityConfiguration(Configuration&lt;SecurityFilterChain&gt; configuration, SecurityFilterChainFactory factory) { configuration.add(factory.createChain(&quot;/admin/**&quot;).add(factory.authc()).add(factory.ssl()).build()); } .... } </code></pre></noscript>

En cualquiera de los dos casos mostrados en este ejemplo se obliga a usar https en la página de login:

El ejemplo completo puede encontrarse en la aplicación del libro PlugIn Tapestry que puedes descargar y probar de mi repositorio de GitHub. Por supuesto también puedes descargar el libro PlugIn Tapestry si quieres conocer de forma más profunda cuales son las características y como funciona Apache Tapestry.

Referencia:
Configurar SSL en un servidor Tomcat, JBoss, WildFly, Lighttpd, nginx o Apache
Generar y convertir claves y certificados con OpenSSL
Libro PlugIn Tapestry

Author: "--"
Send by mail Print  Save  Delicious 
Date: Monday, 18 Aug 2014 22:13

Como todos los meses de agosto, que cantaban los Ixo Rai, llega la semana del puente del 15 de agosto donde son fiestas en muchos pueblos, entre ellos en el mío. Y claro, como las fiestas del pueblo de uno no hay, hay que estar y por eso me tomé festiva la segunda mitad de la semana.

Calle Estaño

Antes de entrar en la realidad paralela que son unas fiestas de pueblo, saqué ratos para empezar a mover la organización de una charla de CachiruloValley a inicios de septiembre. También saqué un rato para pegarle un vistazo a oDesk y completar un poco mi perfil, por ahora muy poco tiempo y sigo sin tener opinión de qué tal es como fuente de proyectos, pero conozco a varios freelancers que les funcionan bastante bien estos servicios de intermediación.

En cuanto a proyectos, algún mail respecto a otros, pero en realidad sólo le di una dedicación seria a Nort:

  • Estuve trabajando tanto en la aplicación móvil, puliendo y refactorizando para hacer el merge de la rama, como en la aplicación web comenzando con una nueva feature. Además la semana pasada tocaba hacer demo al cliente.

Buena semana.

Author: "--"
Send by mail Print  Save  Delicious 
Date: Monday, 18 Aug 2014 20:41
Algunos liks de utilidad que sirven para evaluar esta gran herramienta de CRM, Microsoft Dynamics CRM

Microsoft Dynamics ROI Calculator
Compare Cost, Dynamics, Salesforce, Oracle
Microsoft Dynamics Like Swiss Army
Author: "noreply@blogger.com (JorgeBec)"
Send by mail Print  Save  Delicious 
Date: Monday, 18 Aug 2014 20:34
Es muy fácil que como PM nos perdamos en el día a día, en lo urgente y no en lo importante pero es vital que los PMs den un seguimiento adecuado a sus proyectos a través de sus principales métricas como son los riesgos. Aquí un buen artículo con algunos tips.
Author: "noreply@blogger.com (JorgeBec)"
Send by mail Print  Save  Delicious 
Date: Wednesday, 13 Aug 2014 15:22

RubyConf ArgentinaYa están a la venta las entradas Pajarito ($ 650 pesos argentinos) de una de las mejores conferencias de Ruby del universo conocido: RubyConf Argentina. La conferencia se realiza el 24 y 25 de Octubre en el Centro Cultural Konex en Buenos Aires.

RubyConf Argentina es uno de los mayores eventos de sudamérica sobre desarrollo de software, donde más de 450 desarrolladores se reúnen para aprender, discutir y compartir ideas sobre software libre, web, aplicaciones móviles, seguridad, robótica, y mucho más.

La conferencia reune rubistas y no rubistas de todas partes del mundo, y es una de las puertas de entrada más directas a la cariñosa y cálida comunidad Ruby de América Latina. Va a ser la tercera edición a la que voy, y desde un principio la disfruté como si fuera mi navidad. Recomendada ampliamente.

Como comentaba más arriba están disponibles las entradas a precios promocionales que pueden ir comprando acá. Por otro lado, está abierto el llamado a charlas, así que si tienen ganas de presentar algo para hablar en la conferencia, es su oportunidad.

Uruguayos interesados en cruzar el charco para asistir a la conferencia somos varios. Desde la comunidad de rubistas estamos alentando a más gente a unirse a nosotros y sumarse a la ida en masa al Centro Cultural Konex. De repente conseguimos algun descuento en transporte y alojamiento al ser muchos. Así que si consideraban ir a RubyConf Argentina pero tenían dudas, ya no hay excusas, contáctennos, súmense al meetup, y vénganse con nosotros a RubyConf Argentina \o/

Office Space

 

Author: "--"
Send by mail Print  Save  Delicious 
Date: Tuesday, 12 Aug 2014 17:26

No, no es que haya estado de vacaciones, que por aquí aún se van a hacer esperar al menos un par de meses; aunque algún día suelto caerá, habiendo compromisos ya se sabe como es la vida del autónomo. Y ahí va nueva retro, quincenal otra vez.

Esta vez empecé en Noja hace 2 semanas, viaje para trabajar en la conceptualización de un nuevo proyecto y definir el alcance de un pequeño MVP inicial, estuve asimilando lo que hace una herramienta offline para pasar parte de su funcionamiento al online. Por el momento no puedo decir gran cosa de la temática, como mínimo hasta Septiembre.

Después estuve en Bilbao un par de días, trabajando en las oficinas de mhop y en las de init. Aprovechando también para ver gente, al ser verano estaba claro que iba a ser más complicado encontrarse con muchos; pero aún nos juntamos unos cuantos del gremio a tomar unos zuritos y unos pintxos.

A la vuelta a Zaragoza y sin mucho descanso, como todos los últimos jueves del mes, había zaragozarb, en esta ocasión con una charla/resumen del Rails Open Space que organizó AspGems. Y el finde estuve de visita en mi pueblo, que necesitaba recuperar tras una semana bastante agotadora… aunque terminé hablando de posibles proyectos.

La siguiente semana fue bastante más tranquila, que los viajes me descolocan bastante, ya no estoy acostumbrado de ir de aquí para allá. Saqué tiempo para pasarme a trabajar un rato por por The Garage Of Code, al fin, que hacía mucho que tenía pendiente pasar a visitar su oficina y asistí a las GeeksTalks de este mes.

Sobre proyectos actuales, aparte del que estuvimos conceptualizando:

  • En mhop trabajé en resolver un par de bugs, implementado algunas pequeñas funcionalidades demandadas desde gestión de contenidos y estuve haciendo modificaciones en la gestión de productos por parte de los diseñadores.
  • Estuvimos viendo los cambios en la nueva landing de minchador y estuve hablando con algunos restaurantes. Desde luego que son malas fechas para los restaurantes con los que he hablado: o están de temporada alta o están cerrados/van a cerrar en Agosto. Que no están por la labor, vamos.
  • El grueso de mi dedicación fue de nuevo para Nort. La mayor parte trabajando en la aplicación móvil, principalmente para acceder a varias características nativas y teniendo que usar varios plugins de Apache Cordova para ello. También me tocó hacer algunos pequeños cambios en la aplicación Grails para ajustar algunas cosas del backend.
  • Dediqué algo de tiempo en sentarme a trabajar en la web de Biera Solutions. Empezar a definir objetivos y hacer algunos bocetos en papel para trabajar algunas ideas.

Buena semana.

Author: "--"
Send by mail Print  Save  Delicious 
Date: Sunday, 10 Aug 2014 08:14
En contra de lo que su nombre parece indicar, PhpStorm, no es solamente un entorno de desarrollo PHP, sino que además soporta desarrollo web en general. Es decir, combina las WebStorm con funcionalidades específicas para PHP. Parte de la misma base de WebStorm, por lo que mucha de las cosas que ya viéramos se siguen [...]
Author: "--"
Send by mail Print  Save  Delicious 
Date: Friday, 08 Aug 2014 06:23

TL; DR: Scrum y Kanban. Se complementan.

Habitualmente cuando participo en una conversación donde se afirman cosas como “yo prefiero Scrum”, pues yo prefiero “Kanban” lo que realmente se suele estar diciendo es “a mí me gusta hacer sprints”, “pues yo prefiero no hacerlos”. Si rascas un poco, resulta que el “equipo kanban” hace retrospectivas y el “equipo scrum” tiene un panel visualizando el Value Stream Map.

Según voy practicando y profundizando en Scrum, Kanban o lo que sea, voy cambiando mi visión sobre en qué consiste exactamente la técnica. Esto me ocurre a todos los niveles y todo el rato. Y me seguirá ocurriendo hasta el fin de los tiempos. En concreto, ahora mismo de Scrum y Kanban pienso lo siguiente:

kanban

scrum

Si eres de los que todavía mantiene peleas internas(o externas) sobre si aplicar Scrum o aplicar Kanban porque piensas que son diferentes sabores del agilismo, que sepas que desde mi punto de vista son totalmente combinables. Sobre como se relacionan o si son combinables, por supuesto, hay mucho escrito:

 

 

Author: "--"
Send by mail Print  Save  Delicious 
Date: Thursday, 07 Aug 2014 16:00

Persiguiendo su objetivo de hacer del mundo un lugar mejor, el grupo de super héroes conocido como Los Veng… D. A. T. A. presentó la aplicación Por Mi Barrio, aplicación basada en el software libre Fix My Street de My Society.

¿Recuerdan todas esas veces que gritaron a los cuatro vientos al encontrar un pozo enorme andando en bicicleta o un caño roto desperdiciando agua a litros o un semáforo que estuvo sin funcionar durante días? Por Mi Barrio es la aplicación que pone al alcance de un clic el poder de hacer algo productivo con esa queja al viento.

Por Mi Barrio

Por Mi Barrio

Del sitio web de D.A.T.A.:

Por Mi Barrio, una plataforma que permitirá a las personas que viven en Montevideo enviar reportes sobre daños, desperfectos, vandalismo y otros problemas de nuestra ciudad desde su computadora o celular.

Es decir, cuando encuentres un problema en tu barrio (un pozo en la calle, problemas de arbolado o de iluminación, contenedores de basura rotos, etc.) vas a poder entrar en pormibarrio.uy desde tu computadora (ceibalitas incluidas) o celular y reportarlo. Para hacer esto, sólo tenés que localizar el problema en un mapa. También se pueden subir fotos y comentarios.

Cuando vuelvas a entrar a la página web vas a poder ver tu reporte marcado en un mapa, así como los de los/as demás usuarios/as. También podrás ver los reportes que la Intendencia de Montevideo (IM) marcó como solucionados y los que todavía son problemas para el barrio.

Por Mi Barrio se conecta al Sistema Único de Reclamos de la IM, por lo que se asegura que los reportes denunciados van a llegar a la división correspondiente de la comuna y permite recibir notificaciones sobre respuestas de la IM. ¿Se dan cuenta el poder que le brinda a los ciudadanos? Al reportar los incidentes, los ciudadanos obtienen total transparencia en el progreso del reporte, teniendo visibilidad sobre el trabajo de su gobierno local. Con herramientas como esta, no hay excusas para no involucrarse y formar parte de la sociedad civil.

El proyecto es parte de una iniciativa a mayor escala, así que probablemente habrán novedades entorno al proyecto y se irán viendo proyectos similares. Cuanto más gente lo use, mejor.

Pueden mantenerse al tanto de las noticias de DATA en su sitio web o su cuenta de Twitter.

Author: "--"
Send by mail Print  Save  Delicious 
Date: Wednesday, 06 Aug 2014 16:00
fernandobriano.com

fernandobriano.com

Vengo usando mi sitio personal como “tarjeta de presentación” desde hace un buen tiempo, para dirigir potenciales relaciones laborales a ese sitio. Hace mucho que venía con ganas de actualizarlo así que volví a arrancar de cero (por tercera o cuarta vez).

El contenido es bastante simple, debía ser un sitio que mostrara parte de mi trabajo y experiencia de manera sencilla y directa. No inclui mi CV. Tengo perfiles en LinkedIn y StackOverflow Careers, por lo que mostrar un CV online implica repetir información que ya está en otros lugares. Con cada cambio debería mantener la información actualizada en varios sitios. Así que agregué los links.

También agregué el link a GitHub, donde se puede ver gran parte del trabajo Open Source que he hecho, y otros perfiles online como Lanyrd o Speakerdeck. Las empresas se están acostumbrando a  que pueden encontrar mucha información de un programador online sin tener que pedir específicamente un archivo PDF con la especificación técnica de la persona.

Sería interesante interactuar con la API de LinkedIn o StackOverflow Careers (si la tiene), para tener un CV actualizado “dinámicamente”. Es algo a pensar, junto a la cantidad indeterminada de veces que pienso en borrar mi perfil de LinkedIn en el día. No me aporta mucho más que Spam de gente que no conozco y con la que nunca tuve relación laboral agregándome con el mensaje por defecto de “Me gustaría agregarte a mi red profesional” o algo así. En un momento pensé empezar a responder a cada una de las “invitaciones” con un simple: “¿Por qué?”, pero seguro encuentro cosas mejores que hacer con mi tiempo.

Desde la parte técnica, aprovecho cada reboot de mi sitio personal para usar al menos una tecnología que no conociera de antes. Mi caso de uso era bastante sencillo:

  • HAML para el HTML (por preferencia personal nomás)
  • Markdown para el contenido (ídem anterior)
  • Contenido internacionalizado para Inglés y Español
  • No voy a actualizar el sitio tan seguido ni tengo tanta información, un CMS o similar es demasiado
  • Tiene que ser rápido – El CSS y (en caso de usarlo) JavaScript debe ser lo mínimo necesario.

Tecnología

Vamos a las decisiones tecnológicas que tomé y el por qué. Para empezar, decidí que iba a usar Cuba y me iba a armar un “mini-cms” como para mantener el sitio actualizado cuando quiera agregar alguna cosa nueva. El sitio era una aplicación Sinatra alojada en Heroku. No usaba base de datos, al ser poco el contenido se encontraba en archivos de texto (yml). Así que empezar de cero con Cuba no iba a cambiar mucho el tema de hosting, podía usar Postgres o alguna otra cosa en Heroku.

A mitad de camino volví a pensar en generadores de sitios estáticos. Ya había hecho algunas pruebas antes con Jekyll, y empecé a leer sobre Middleman. Navegando un poco por la documentación, me decidí a usarlo. Al producir páginas estáticas, podía alojar el sitio en GitHub Pages en vez de Heroku. De esta forma, dependo de un repo menos.

ruby-middleman

Templates

Middleman soporta templates Slim, ERB, HAML y más. También trae soporte para Markdown con distintos motores, así que resolvía dos de las cosas que quería en el sitio. Trae también soporte para partials y Template Helpers que nos facilitan algunas tareas y definir helpers propios es bastante sencillo. En mi caso usé los helpers propios para incluir algunos fragmentos de código Ruby que tenía por ahí para recuperar información de distintos sitios: RubyGems, GitHub y WordPress.

Esto genera una lista “dinámica” obteniendo información de cosas que he publicado (gemas, plugins, repos) y lo muestra en el sitio.

Contenido

Haml es malo para escribir contenido, y prefiero usar Markdown siempre que puedo. La idea era combinar estas dos cosas con la internacionalización. Así que el contenido se encuentra en archivos yml correspondientes a los locales ‘es’ y ‘en’ para español e inglés respectivamente. Al usar i18n, llamamos a los textos con el método t(:clave_del_texto). El siguiente paso era parsear el texto como markdown, por lo que hice un helper parecido llamado text, con el cual renderizo como Markdown lo que me devuelve el método t:

def text(string)
  markdown = Redcarpet::Markdown.new(
                                     Redcarpet::Render::HTML,
                                     autolink: true,
                                     no_intra_emphasis: true,
                                     lax_spacing: true)
  markdown.render(t(string))
end

CSS y JavaScript

El CSS es muy sencillo, menos de 200 líneas aunque inclui el framework SimpleGrid para las grillas y diseño responsivo. Otras veces había usado PureCSS, pero decidí usar algo distinto esta vez. También agregué los íconos de Octicons y Genericons para darle algo más de vida al sitio.

Middleman incluye Sprockets, un sistema de empaquetamiento de recursos (CSS, JavaScript, CoffeeScript). Permite incluir otros archivos en uno, empaquetándolos y minificándolos (opcionalmente) a un solo archivo. De esta forma, puedo incluir simplegrid.css, octicons.css y genericons.css en mi archivo css principal con las siguientes sentencias:

/*
 *= require _simplegrid
 *= require _octicons.css
 *= require _genericons.css
 */

Al agregar el guión bajo antes del nombre de cada archivo, estoy pidiendo que los contenidos de ese css sean concatenados al contenido de mi css para tener todo en un mismo archivo.

Lo mismo se puede hacer con JavaScript. Lo bueno de esto es que a pesar de tener muy poco código, puedo organizarlo en archivos distintos. Y nada de esto afecta al rendimiento final del sitio ya que todo el contenido se va a servir como recursos estáticos generados previamente.

Publicación en GitHub Pages

Una parte que le falta al código es la generación automática de los archivos estáticos de manera periódica. Por ahora estoy usando la gema middleman-gh-pages desarrollada por algunos ex Neo y otros desarrolladores de la comunidad. La funcionalidad básica que provee la gema, es una tarea Rake para poder publicar a la branch gh-pages en GitHub con un único comando:

$ rake publish

Conclusión

Llegué a un proceso de actualización bastante dinámico y el resultado con un sitio estático alojado en GitHub Pages es suficientemente bueno. Cumple su cometido de ser un centro de enlaces a distintos recursos que permitirían a potenciales colegas o contratadores conocer un poco sobre mi perfil laboral.

El resultado final está en fernandobriano.com y el código fuente en GitHub. Cosas para mejorar siempre hay, ya habrá tiempo para eso :)

 

Author: "--"
Send by mail Print  Save  Delicious 
Date: Wednesday, 06 Aug 2014 06:20

Lo hablaba ayer con David:

  • mientras no sé, es cuando tengo el cerebro a tope y me apetece escribir. Entonces escribo sobre lo que no sé.
  • cuando ya sé, no me da ninguna vidilla escribir. No escribo sobre lo que realmente sé.
  • si escribo cosas complicadas y sesudas, no explico los conceptos en los que baso, y no se entiende.
  • si escribo sobre los conceptos básicos, siento que el post es demasiado básico como para ser útil….

Y así, todo el día… :-P

Mi conclusión es que prefiero escribir, aunque sea artículos tontainas como éste, pero intentar mantener un flujo continuo de artículos que me ayuden a ir rumiando ideas e ir contrastándolas con la gente…

Author: "--"
Send by mail Print  Save  Delicious 
Date: Tuesday, 05 Aug 2014 08:49


Es un ejercicio típico, pero que casi siempre se ve teóricamente, pero mira, vamos a traerlo a la práctica. Lo vamos a implementar con semáforos. Como vemos aquí y aquí, los semáforos, entre otras cosas nos servirán para bloquear un proceso o un thread que trata de acceder a un recurso crítico que está siendo usado por otro proceso, un ejemplo típico es un baño público, cuando la puerta no está bloqueada, pasas, la bloqueas, haces lo que tengas que hacer allí, y luego desbloqueas la puerta y te vas. Aquí todo funciona parecido, cuando tienes que acceder a un recurso, miras si el semáforo está abierto, y si es así, lo cierras, utilizas ese recurso y cuando terminas lo abres.
Pero claro, podemos crear todos los semáforos que queramos y tenemos total libertad para hacer que los procesos esperen que los semáforos estén abiertos o los abran, por lo que podemos crear multitud de situaciones.

Pero claro, ¿qué puede pasar si tenemos tres procesos (P1, P2 y P3) y se da el caso en el que P1 esté esperando a P2, P2 esté esperando a P3 y P3 esté esperando a P1? Pues nada, estaremos esperando indefinidamente.
El caso típico es el siguiente:

  • Tenemos dos procesos (P1 y P2)
  • Tenemos dos recursos (R1 y R2) de acceso exclusivo (sólo pueden ser accedidas por un proceso cada vez)
  • P1 quiere acceder a R1 por lo tanto cierra su semáforo
  • P2 quiere acceder a R2 por lo tanto cierra su semáforo
  • P1 quiere acceder también a R2 por lo que espera a que su semáforo esté abierto
  • P2 quiere acceder también a R1 por lo que espera a que su semáforo esté abierto

Como P1 está esperando a que P2 libere el semáforo de R2 y P2 está esperando a que P1 libere el semáforo de R1, los dos se van a quedar indefinidamente así.

Si queremos comprobarlo:

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
63
64
65
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <string.h>

int main()
{
  sem_t *sem1 = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  sem_t *sem2 = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
             MAP_SHARED | MAP_ANONYMOUS, -1, 0);

  int child;

  sem_init (sem1, 1, 1);
  sem_init (sem2, 1, 1);

  child = fork();
  if (child==-1)
    exit(1);
  else if (child==0)
    {
      while(1)
    {
      printf ("[%d] Child waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Child passes sem1.\n", getpid());
      printf ("[%d] Child waits for sem2...\n", getpid());
      sem_wait(sem2);
      printf ("[%d] Child passes sem2.\n", getpid());
      usleep(100);
      printf ("[%d] Child posts sem2\n", getpid());
      sem_post(sem2);
      printf ("[%d] Child posts sem1\n", getpid());
      sem_post(sem1);
    }
      exit(2);
    }
  else
    {
      while(1)
    {
      printf ("[%d] Main waits for sem2...\n", getpid());
      sem_wait(sem2);
      printf ("[%d] Main passes sem2.\n", getpid());
      printf ("[%d] Main waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Main passes sem1.\n", getpid());
      usleep(100);
      printf ("[%d] Main posts sem1\n", getpid());
      sem_post(sem1);
      printf ("[%d] Main posts sem2\n", getpid());
      sem_post(sem2);
    }
    }
  while (wait(NULL)>=0);

  munmap(sem1, sizeof(sem_t));
  munmap(sem2, sizeof(sem_t));

  return 0;
}

Si compilamos (con pthread: gcc -o deadlock deadlock.c -lpthread) veremos algo como esto:

$ ./deadlock
[30643] Main waits for sem2…
[30643] Main passes sem2.
[30643] Main waits for sem1…
[30643] Main passes sem1.
[30644] Child waits for sem1…
[30643] Main posts sem1
[30643] Main posts sem2
[30643] Main waits for sem2…
[30643] Main passes sem2.
[30644] Child passes sem1.
[30643] Main waits for sem1…
[30644] Child waits for sem2…

También es verdad que no siempre es tan rápido, a veces pueden darse muchas iteraciones hasta que se produce el bloqueo, otras veces a la primera iteración se produce, depende de cómo de rápidos sean los procesos, pero tarde o temprano se va a dar. Para más información sobre esto, tenemos la página de Wikipedia.

Para dar algo de teoría, en este ejemplo se han cumplido las 4 condiciones necesarias (tienen que cumplirse todas ellas) de Coffman:

  • Exclusión mutua: Los recursos R1 y R2 son exclusivos, sólo pueden ser accedidos por un proceso cada vez
  • Retención y espera: P1 ha adquirido un recurso R1 y lo retiene mientras espera a que R2 (adquirido por P2) se libere
  • No expropiación: P1, por ejemplo, no puede quitarle R2 a P2 ni al revés.
  • Espera circular: P1 está esperando a P2 y P2 espera a P1.

¿Cómo lo arreglamos?
Aquí podemos buscar tantas soluciones como nos dé la imaginación, pero vamos a proponer algunas más típicas:

Guardar el orden de reserva de recursos

Si siempre vamos a utilizar R1 y R2, los pedimos siempre en el mismo orden:

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
...
  child = fork();
  if (child==-1)
    exit(1);
  else if (child==0)
    {
      while(1)
    {
      printf ("[%d] Child waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Child passes sem1.\n", getpid());
      printf ("[%d] Child waits for sem2...\n", getpid());
      sem_wait(sem2);
      printf ("[%d] Child passes sem2.\n", getpid());
      usleep(100);
      printf ("[%d] Child posts sem2\n", getpid());
      sem_post(sem2);
      printf ("[%d] Child posts sem1\n", getpid());
      sem_post(sem1);
    }
      exit(2);
    }
  else
    {
      while(1)
    {
      printf ("[%d] Main waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Main passes sem1.\n", getpid());
      printf ("[%d] Main waits for sem2...\n", getpid());
      sem_wait(sem2);
      printf ("[%d] Main passes sem2.\n", getpid());
      usleep(100);
      printf ("[%d] Main posts sem2\n", getpid());
      sem_post(sem2);
      printf ("[%d] Main posts sem1\n", getpid());
      sem_post(sem1);
    }
    }
...

No liarla demasiado

Utilizar la mínima cantidad de semáforos posible, si vemos que con sólo uno basta, pues ponemos uno. En este caso sí bastaría con uno, pero habrá casos en los que no se pueda y tendremos que hacer otras cosas…

Si se puede se puede, si no, no

Consiste en utilizar sem_trywait(), si el recurso está ocupado devolverá error, pero no bloqueará. Sólo haciéndolo en uno de los procesos vale, pero claro este proceso entrará menos veces en la sección crítica:

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
...
  child = fork();
  if (child==-1)
    exit(1);
  else if (child==0)
    {
      while(1)
    {
      printf ("[%d] Child waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Child passes sem1.\n", getpid());
      printf ("[%d] Child waits for sem2...\n", getpid());
      try = sem_trywait(sem2);
      if (try==0)
        {
          printf ("[%d] Child passes sem2.\n", getpid());
          usleep(100);
          printf ("[%d] Child posts sem2\n", getpid());
          sem_post(sem2);
        }
      else
        printf ("[%d] sem2 busy\n", getpid());
      printf ("[%d] Child posts sem1\n", getpid());
      sem_post(sem1);
    }
      exit(2);
    }
  else
    {
      while(1)
    {
      printf ("[%d] Main waits for sem2...\n", getpid());
      sem_wait(sem2);
      printf ("[%d] Main passes sem2.\n", getpid());
      printf ("[%d] Main waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Main passes sem1.\n", getpid());
      usleep(100);
      printf ("[%d] Main posts sem1\n", getpid());
      sem_post(sem1);
      printf ("[%d] Main posts sem2\n", getpid());
      sem_post(sem2);
    }
    }
...

Timeouts

De la misma forma que probamos si estaba disponible el recurso o no, ahora probamos con un timeout, hacemos que el proceso sea capaz de esperar un tiempo a ver si el recurso se libera, aunque pasado ese tiempo dejará de esperar:

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
  child = fork();
  if (child==-1)
    exit(1);
  else if (child==0)
    {
      while(1)
    {
      printf ("[%d] Child waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Child passes sem1.\n", getpid());
      printf ("[%d] Child waits for sem2...\n", getpid());
      timeout.tv_sec=0;
      timeout.tv_nsec=100000;
      try = sem_timedwait(sem2, &timeout);
      if (try==0)
        {
          printf ("[%d] Child passes sem2.\n", getpid());
          usleep(100);
          printf ("[%d] Child posts sem2\n", getpid());
          sem_post(sem2);
        }
      else
        printf ("[%d] sem2 busy\n", getpid());
      printf ("[%d] Child posts sem1\n", getpid());
      sem_post(sem1);
    }
      exit(2);
    }
  else
    {
      while(1)
    {
      printf ("[%d] Main waits for sem2...\n", getpid());
      sem_wait(sem2);
      printf ("[%d] Main passes sem2.\n", getpid());
      printf ("[%d] Main waits for sem1...\n", getpid());
      sem_wait(sem1);
      printf ("[%d] Main passes sem1.\n", getpid());
      usleep(100);
      printf ("[%d] Main posts sem1\n", getpid());
      sem_post(sem1);
      printf ("[%d] Main posts sem2\n", getpid());
      sem_post(sem2);
    }
    }

Aquí con un struct timespect timeout donde ponemos el tiempo en nanosegundos (variable tv_nsec) creamos una espera de un tiempo máximo para el recurso reservado.

Otros algoritmos

Para solucionar esto, dependiendo de los casos tenemos otros algoritmos, que espero dedicar posts próximamente.
Foto: Moosealope (Flickr) CC-by

La entrada Deadlock, bloqueo mutuo, abrazo mortal o cómo colgar un proceso con esperas infinitas. ☠ ☠ ☠ aparece primero en Poesía Binaria.

Author: "--"
Send by mail Print  Save  Delicious 
Date: Monday, 04 Aug 2014 06:56
Si os digo que FORTRAN (Formula Translating System) fue un lenguaje diseñado por IBM en los años 50 para poder realizar programas científicos que resultaran eficientes, probablemente no os estaré contando nada que no sepáis. En aquellos años solamente existía el ensamblador, y Fortran representaba el primer lenguaje más o menos estandarizado de alto nivel. [...]
Author: "--"
Send by mail Print  Save  Delicious 
Date: Saturday, 02 Aug 2014 10:14

Infrastructure as code, mantener todos los cambios en tus servidores bajo control completo, configurar tus servidores escribiendo recetas, tener tu infraestructura en tu sistema de control de versiones, que tus desarrolladores puedan utilizar una máquina igual que la de producción en su equipo para evitar el “en mi máquina funciona”.
Bien, compro. ¿Donde hay que firmar?. Take my money.
Pero no. Por ahora no.

Te invito a hacerte estas preguntas, que yo me hice demasiado tarde:
-¿Cada cuanto tiempo levantas un servidor nuevo?
Nosotros usamos Amazon AWS, si, es cloud, pero eso no quiere decir que andemos escalando y desescalando nuestra arquitectura cada 15 minutos. Además, cuando necesito un servidor igual que el de producción levanto una imagen del propio servidor de producción. Además nuestras aplicaciones no están preparadas para escalar horizontalmente por ahora, nuestros servidores no son servidores stateless.
-¿Cuanto tiempo te lleva configurar un servidor nuevo?
A día de hoy, un servidor para correr un stack LAMP es relativamente trivial, con mantener el fichero de configuración de Apache en un control de versiones tendrías todo bastante avanzado. Para software mas complejo como stacks Java tras un Tomcat y un Apache la cosa no es mucho mas complicada y si lanzas un producto cada 6 meses puede que escribir la receta te lleve mas tiempo.
-¿Cuanto tiene que cambiar tu equipo de desarrollo para que todo esto funcione correctamente?¿Qué les aporta a tus desarrolladores disponer de un entorno igual al de producción en su máquina? y por último ¿podrian levantar un entorno de esas características en sus máquinas?
Si trabajas con lenguajes interpretados y demás (PHP por ejemplo) es muy bonito, levantas una máquina compartes la carpeta en la que va el código apuntas tu IDE a dicha carpeta y voilá, pero ¿y si trabajas con Java? ¿para debuggear como lo haces? puedes levantar el Tomcat en modo debug o abrir el puerto, pero ya estás empezando a modificar la máquina y no se parecerá tanto a la máquina de producción.
Si tu producto es la conjunción de una serie de productos ¿haces levantar al desarrollador tus API, tu servicio de SSO…?
-¿Cuanto te va a costar formar a la gente suficiente para que esto sea sostenible?
Infrastructure as code es un matrimonio en toda regla, te comprometes estrictamente a gestionar tu infraestructura de esta manera ya que sino las incosistencias te volverán loco (hay textos que sugieren que ni siquiera te proporciones acceso SSH a tus propios servidores para evitar tocar a mano).

A día de hoy, estas barreras no me aportan la suficiente mejora sobre lo que tengo hasta ahora, me supone mas problema mantener bajo control los ficheros de configuración de mi aplicación que el software que instalo en mis máquinas. Me ha resultado muy costoso adaptar toda la infraestructura ya existente a “recetas” que pudiesen crearla desde cero y no solo eso, sinó que los servidores que ya he instalado “a mano” tendrian que entrar en dicho ciclo o bien debería levantarlos en paralelo con las nuevas recetas y hacer el switch, por ahora el beneficio no me justifica el riesgo.

Pero ojo, evidentemente y si tanto se oye hablar de ello, por algo será y he visto muy buenas motivaciones para adoptar estas técnicas y tecnologías.
-Imagina que realizas instalaciones en servidores alojados en las instalaciones de tus clientes (appliance), tener perfectamente controladas las versiones y modificaciones que realizas en todos y cada uno, poder propagar los cambios basados en roles u otras características.
-Necesitas levantar máquinas automáticamente en un proveedor de hosting que no te proporcione características como la creación de imágenes personalizadas para poder escalar horizontalmente.
-Si tu stack tecnológico te lo permite para trabajar en local, en uno de nuestros proyectos el stack encajaba muy bien y con esto hemos proporcionado a los desarrolladores una manera de probar en un servidor con la misma configuración que el de producción todo el producto.

Author: "--"
Send by mail Print  Save  Delicious 
Date: Saturday, 02 Aug 2014 09:47

IMG_20140802_110438

Repaso en voz alta los diferentes pasos a la hora de servir una web (lo siento pero no soy capaz de dibujar con un ordenador con la misma comodidad que lo hago con papel y boli):

t0: tiempo de envío de la petición desde el navegador al servidor, desde que hacemos click hasta que dicha petición llega al servidor, este factor es ajeno a la web como tal, pero hay que saber identificarlo porque el problema estará en la conexión del usuario y por lo tanto no tendrá que ver con nuestra aplicación directamente.
t1: tiempo de procesado de la petición, primera fase de la petición, recepción de petición, cookies y demás información, procesado de esta, checkeo de la corrección, los factores que pueden influenciar en ella son: código que es demasiado lento o hardware que no soporta el número de peticiones o tipo de peticiones que se reciben.
t2: tiempo para enviar la petición desde el código hasta el sistema de persistencia (generalmente una BBDD). Es habitual que el código y la información no estén en la misma máquina, en ese momento la conectividad entre estas es un punto mas a medir, es un problema de red.
t3: tiempo de procesado de los datos en el sistema de persistencia. ¿Que tipo de consultas hacemos a la BBDD? ¿pedimos sólo lo que necesitamos? ¿está la consulta hecha de la mejor manera? ¿están los datos estructurados de la mejor manera? Si recibo los datos con frecuencia de quince minutos pero solo permito consultarlos por franjas horarias (el datepicker de la interfaz no hila mas fino) ¿tiene sentido almacenar un registro por cada 15 minutos?

timestamp0,16:00,v0
timestamp1,16:15,v1
timestamp2,16:30,v2
timestamp3,16:45,v3

Vs.

timestamp0,16:00,{v0,v1,v2,v3}

En este sencillo ejemplo, en la segunda estructura se “buceará” entre 1/4 del número de registros (extrapolemos el mismo caso para registros minutales o segundales) para obtener la misma información que si se bucease en la primera. Esta decisión está relacionada con las estructuras de la información (inferidas de un entendimiento claro de la solución que queremos dar al cliente), de una consulta correcta al sistema de persistencia y por último del hardware.
t4: devolución de datos al código, aquí entran dos factores, la red, como en el momento del envío de la query y por otro lado el volúmen de datos que se devuelvan (v4), si volvemos al ejemplo del punto t3 el volúmen de datos a transferir será menor. Aquí tendremos que buscar la relación correcta entre lo que cueste conseguir los datos lo mas filtrados posible y el volumen que nos permita transferir el ancho de banda en un tiempo aceptable.
t5: procesado de datos para generar la respuesta, se trata de un problema de código intimamente relacionado con el volumen de datos que haya llegado del sistema de persistencia, habrá ocasiones en las que traer mas datos y procesarlos por código sea mas interesante que traer los datos estrictamente necesarios a costa de haber trasladado demasiada carga al sistema de persistencia, es un problema de código y hardware.
t6: tiempo de devolución de datos, se trata del volumen de información (v6) que se envía al navegador, cuanto menor mejor evidentemente, aquí influye nuestro código y también el ancho de banda.
t7: tiempo de renderizado en el navegador, si enviamos mucha información al navegador en un formato que depués deba ser procesada (una librería de graficado JS a la que servimos listas de puntos) tendremos esa sensación de “la página ha cargado pero no veo nada”.
#: número de consultas al sistema de persistencia que se realizan para poder obtener toda la información del sistema de persistencia, aquí hay que realizar las pertinentes pruebas para cada caso, hay veces que 5 consultas sencillas serán mas rápidas que una consulta compleja (clásicos JOIN en BBDD relacionales que unen tablas de tamaños bestiales para ahorrar hacer unas pocas queries contra tabla única). He marcado este punto en rojo porque aquí hay un problema serio con el código, la utilización de grandes librerias de acceso a datos a veces genera una “niebla” sobre lo que realmente ocurre con nuestra BBDD. Un caso real es el del borrado de todos los hombres de una tabla usuarios que dispone de celda sexo que se realizó obteniendo los id únicos de cada usuario de sexo hombre para realizar el borrado en lugar de un simple borrado por sexo.

Author: "--"
Send by mail Print  Save  Delicious 
Next page
» You can also retrieve older items : Read
» © All content and copyrights belong to their respective authors.«
» © FeedShow - Online RSS Feeds Reader