miércoles, mayo 28, 2014

Yo vs emprendimientos TI

Inicialmente iba a ser un artículo lleno de argumentos y explicaciones. Era sobre mi propio enfoque y el porqué de mi incompatibilidad con los emprendimientos. Al final el éxito de un emprendimiento se resume en una sola cosa: 
SACRIFICIO.

Hace algún tiempo tuve la oportunidad de conversar en extenso con un suizo, residente en Chile. La conversación con este exitoso empresario del rubro inmobiliario pronto derivó en su visión sobre los emprendimientos en Chile.
Me comentó sobre una experiencia que tuvo con unos amigos de él. Tenían una idea muy buena, auspiciosa en cuanto al mercado y a las potenciales ganancias a obtener. Él con su experiencia les señaló:
"La idea es buena, tiene potencial, pero para poder llevarla a cabo tienen que destinar al menos 10 horas diarias para trabajar en ella."
Se podrán imaginar que la réplica fue que no podían destinar 10 horas diarias de trabajo, ya que todos los involucrados podían entregar dedicación parcial al proyecto, debido a que trabajaban jornada completa.

Me arriesgo a decir que un alto porcentaje de los emprendimientos que quedan en nada (léase sólo en la idea y las buenas intenciones) se debe a que los integrantes no pueden destinar el tiempo necesario para trabajar en el proyecto.
Lamentablemente los emprendimientos no pueden llevarse a cabo a ritmo propio o cuando tengas ganas/puedas de dedicar tiempo a ello.

Finalmente todo se reduce a SACRIFICIO. ¿Cuánto estás dispuesto a sacrificar?:
  • ¿Sacrificarías no ver a las personas que amas? 
  • ¿Sacrificarías horas de sueño y descanso? 
  • ¿Sacrificarías dejar de ganar una remuneración por hacer lo mismo (o similar)? Nota: Puse dejar de ganar y no perder, hay una sutil diferencia.
  • ¿Sacrificarías rendir en tu trabajo actual por dedicar tiempo a tu emprendimiento? y de paso asumir el costo que esto puede significar (normalmente ser despedido).
Y quedo corto en este listado. Responder NO a cualquiera de estas preguntas ya es un riesgo que no estás dispuesto a tomar.

¿Cuánto estás dispuesto a sacrificar?
¿Cuánto?


En mi caso las cuentas eran negativas, demasiadas responsabilidades en mi quehacer cotidiano, repercusiones familiares, costo de oportunidad descompensado y una que por donde la mirara tenía demasiado peso. Después de una jornada de 8 horas diarias frente a un computador, resolviendo problemas de las más variadas especies, física y mentalmente termino disminuido para seguir haciendo lo mismo en casa (fuera de horario).

Hay que ser sincero con uno mismo y no tratar de auto-convencerse con falsas ilusiones. Las charlas TED y de la universidad suenan muy lindas y convincentes, pero llevarlas a la práctica dependerá de realidad personal de cada uno.


Spring REST Services y el error Required String parameter is not present

Desarrollando una API REST utilizando Spring MVC, y consumiéndola con AngularJS, me enfrenté al problema "Required String parameter is not present". Así es como lo solucioné.

Hace poco "vendí" (en rigor sólo convencía  mi jefe que es buena idea tomar este enfoque) una idea para abordar nuevos desarrollos. Consiste en desarrollar una API REST, utilizando el generador de código Telosys, y generar las interfaces con HTML5, CSS3 y JavaScript de modo de consumir los servicios con AngularJS.
Nada nuevo dirán, pero ofrece la ventaja que el mismo desarrollo web puede ser portado sin demasiado esfuerzo a una aplicación móvil usando Phonegap, o bien a una aplicación de escritorio utilizando Node-Webkit.

Este tipo de desarrollo resulta bastante rápido, y si logras una combinación entre Angular-UI-router y plantillas web con controladores propios (los controllers de AngularJS), se obtiene una modularidad bastante atractiva.

El problema

El código no tenía problemas, pero al tratar de realizar una llamada POST con Restangular a pesar de que el JSON con los datos que debía recibir la petición estaba bien construido, la llamada arrojaba el error:
Required String parameter 'usr' is not present

Soluciones propuestas

De las soluciones propuestas, después de realizar muchas búsquedas, ninguna funcionó:
  • Forzar que todas las peticiones POST indicaran en sus headers
    Content-Type = 'application/x-www-form-urlencoded' con el código:
  • $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';   
  • Agregar el atributo enctype="application/x-www-form-urlencoded" a la etiqueta form.
  • Realizar la llamada con AngularJS nativo vs usar Restangular.

Hasta pensé en cambiar el enfoque y usar HATEOAS, pero significaba realizar demasiados cambios a la aplicación (en rigor regenerar el código, Telosys se encarga del trabajo sucio).

Solución

Dí con este artículo de StackOverflow, donde explican que el servicio debe recibir un objeto que tenga todos los parámetros de la petición. Algo como:
@RequestMapping(value = "events/add", method = RequestMethod.POST)
public void addEvent(@RequestBody CommandBean commandBean){
    //some code
}

donde se debe indicar que el objeto (puede ser un POJO) CommandBean es el  @RequestBody. Y Spring se encargará de capturar los parámetros adecuadamente.
Y eso funcionó sin problemas.

miércoles, marzo 26, 2014

GulpJS amistoso con AngularJS

Hace poco después de mi lectura diaria descubrí GulpJS. Es una simpática herramienta de línea de comando que permite optimizar muchas cosas del desarrollo web, desde la minimización de imágenes, hasta la "liposucción de código" como le llamamos en la oficina al proceso de eliminar comentarios y mensajes de debug.
El resultado normalmente es un archivo .js minimizado, un archivo .css también minimizado, e imágenes y archivos .html "optimizados".

No les voy a enseñar a usarlo, eso lo pueden ver en detalle en los siguientes enlaces (todos en inglés):
Los primeros problemas a los que me enfrenté fueron básicamente que las configuraciones por defecto son muy agresivas, por lo que mis HTMLs quedaron sin algunos atributos necesarios para que mis controladores Angular funcionaran, y que derechamente mis controladores Angular dejaron de funcionar.

Después de leer algo de documentación para revisar las opciones de los plugins de Gulp que decidí usar, y trás algunas pruebas, llegué al siguiente código, que me ha arrojado los mejores resultados:

// Include gulp
var gulp = require('gulp'); 

// Include Our Plugins
var jshint = require('gulp-jshint');
var concat = require('gulp-concat');
var changed = require('gulp-changed');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var stripDebug = require('gulp-strip-debug');

// Lint Task
gulp.task('lint', function() {
    return gulp.src('js/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
});

// Concatenate & Minify JS
gulp.task('scripts', function() {
    return gulp.src(['js/angular.min.js', 'js/Directive/bindonce.min.js', 'js/jquery-2.0.3.min.js', 'js/bootstrap.min.js',  'js/app.js', 'js/*.js', 'js/Service/*.js', 'js/Filter/*.js', 'js/Directive/*.js', 'js/Controller/*.js'])
        .pipe(concat('appjs.js'))
        .pipe(stripDebug())
        .pipe(gulp.dest('dist'))
        .pipe(rename('app.min.js'))
        .pipe(uglify({ mangle: false }))
        .pipe(gulp.dest('dist'));
});

// include plug-ins
var minifyHTML = require('gulp-minify-html');
 
// minify new or changed HTML pages
gulp.task('htmlpage', function() {
  var htmlSrc = '*.html',
      htmlDst = 'dist';
 
  gulp.src(htmlSrc)
    .pipe(changed(htmlDst))
    .pipe(minifyHTML({ empty: true, spare:true, quotes: true }))
    .pipe(gulp.dest(htmlDst));
});

// include plug-ins
var autoprefix = require('gulp-autoprefixer');
var minifyCSS = require('gulp-minify-css');
 
// CSS concat, auto-prefix and minify
gulp.task('styles', function() {
  gulp.src(['css/bootstrap.min.css', 'css/*.css'])
    .pipe(concat('styles.min.css'))
    .pipe(autoprefix('last 2 versions'))
    .pipe(minifyCSS())
    .pipe(gulp.dest('dist'));
});

// default gulp task
gulp.task('default', ['htmlpage', 'scripts', 'styles'], function() {
});

Lo único que no pude resolver a través de parámetros (y si alguien lo sabe se agradece si comparten el dato) fue el hecho que el plugin minify-html se "come" mis espacios en blanco intencionales (a veces necesarios) y los atributos que no tienen valor (como el bindonce). Tan grave no es si sabes donde ponerlos de vuelta, pero eso ya es parte del trabajo manual.

domingo, diciembre 15, 2013

¿Es el fin de las aplicaciones de escritorio?

Hace poco me encomendaron  el desarrollo de un sistema de software. Inicialmente pensé en usar Java, pero terminé con algo "más web".



Cuando nos enfrentamos al desarrollo de software, aparte del diseño acorde a la captura de requerimientos que se haya realizado, y las interfaces de usuario; entre muchas otras cosas; se debe escoger la tecnología adecuada.

La elección de que tecnología utilizar normalmente va ligada a la instalación, vale decir en las características de la máquina del cliente donde se instalará la aplicación. En la propuesta inicial pensé en realizar el desarrollo usando Java y una base de datos H2. Logré un avance para la generación medianamente rápida de la GUI, sin embargo me di cuenta que para conseguir interfaces de usuario sencillas, y por sobretodo intuitivas, Java me limitaba demasiado, aún considerando Swing, JavaFX y muchas otras librerías de terceros que entregan infinitas alternativas.



La alternativa:

Hacer un desarrollo "más web", vale decir desarrollar el FrontEnd en HTML y el backend en modo "servidor". Entre las elecciones "lógicas" estaba utilizar un servidor Apache Tomcat, un Jetty, o algo para contener JSP y Servlets, o usar Apache (o similar) con PHP.
El problema de esas opciones es que el computador huésped, la máquina del cliente y usuario final, de una u otra manera termina convirtiéndose en un servidor, eso sin considerar la cantidad de dependencias que se deben considerar:
  • JDK de Java
  • Servidor web
  • Motor de base de datos
  • Plugins varios
  • etc.

Pensé ¿.NET será una alternativa? y el problema es similar al de Java, necesitas previamente instalar muchas dependencias. Si el PC del cliente no tiene todo lo necesario, una aplicación que pesa un par de megas significa finalmente descargar sobre 40 MB de instaladores anexos. ¿Cómo evitamos eso?

De vuelta al enfoque inicial

¿Una WebApp local quizás? Con eso se soluciona el peso de la aplicación, aunque hay 2 problemas:
  1. El almacenamiento de datos usando WebDB está limitado a las restricciones del navegador que se utilice. Eso y los riesgos que el usuario utilice distintos navegadores, generando inconsistencias en la información, o que haga una limpieza agresiva del caché del navegador (lo que eliminaría la BD).
  2. La versión del navegador es un elemento que no se puede ignorar, y créanlo, aún hay usuarios de Internet Explorer 6...

Busqué alternativas disponibles, y ahorrándoles el proceso de análisis y selección de la a mi parecer mejor, finalmente llegué a node-webkit.
Aprovechando mi reciente incursión con Node.js decidí hacer algunas pruebas, que luego de resolver un par de problemas han resultado en una aplicación que cumple todas mis expectativas. Lo que utilicé:


Y una vez llegado a esto empiezo a cuestionar que tanto vale la pena desgastarse tratando de hacer una aplicación de escritorio, con miles de dependencias y limitaciones. Más considerando que hoy en día todo avanza hacia convertirse en Web:
  • La comunicación entre las personas se vuelve más y más Web, sólo piensen en las redes sociales.
  • La experiencia de juego cada vez se enlaza más a la Web.
  • Aplicaciones móviles desarrolladas de manera híbrida, vale decir HTML5+CSS3+JavaScript+API nativas. Phonegap lleva mucho avanzado en esta línea.
  • Las aplicaciones de Windows 8, que muchas veces están desarrolladas 100% usando tecnologías web.
  • Los temas de escritorio de GnomeShell utilizan CSS para muchas de sus definiciones.
  • Muchos widgets de escritorio de cada uno de los distintos sistemas operativos
  • El mismo Node.js que es JavaScript en el servidor.
  • Me arriesgaría a decir que más del 90% de los desarrollos de Google.

Incluso escalar este el modelo de aplicación de escritorio web no es tan difícil, pensando en un escenario en el que se decida masificar el uso de la aplicación en la organización.

Estoy claro que esta línea puede no ser la respuesta a todos los problemas que podemos enfrentar en desarrollo de software, pero al menos a mi me hace dar una segunda mirada cada vez que me hablan de desarrollar una aplicación de escritorio.

Si tienen dudas sobretodo respecto a como resolver la persistencia de datos con node-webkit, Sequelize y SQLite no duden en hacerlas.


lunes, noviembre 25, 2013

Cómo rescatar archivos desde un correo en .eml (Linux)

Muchas veces recibimos correos con información adjunta, y también muchas veces nuestros remitentes nos reenvían un archivo de extensión .eml que eventualmente trae uno o muchos archivos adjuntos. Aquí una pequeña fórmula de como recuperar estos archivos adjuntos.

  1. Lo primero determinar cuantos adjuntos pueden venir. A veces bastará  con ejecutar un comando como este:
    grep "filename=" archivo.eml
    que nos mostrará el listado de archivos supuestamente incluidos.
  2. Luego ejecutar el siguiente comando:
    munpack archivo.eml 
    Este comando rescatará el listado de archivos que supuestamente vendría codificado dentro del adjunto.
  3. En casos especiales (como el que me sucedió recién), en los cuales el comando no rescata todos los archivos que se supone trae dentro. En ese caso habrá que abrir el archivo .eml con un visor de archivos de texto y buscar la línea donde aparezca el archivo qeu nos interesa rescatar. por ejemplo:
    Control+F filename="archivo.zip"
    Una vez encontrada la línea, se procede a eliminar todo lo anterior, y volvemos al paso 2. Eso debiera funcionar (debiera...). Esto se puede hacer por línea de comando, pero eso lo dejo a la inquietud del lector :)

Ejecutando un proceso en múltiples procesadores (Linux y Windows)

Muchas veces tenemos a nuestra disposición un servidor con múltiples procesadores, y nos interesaría poder aprovecharlos ejecutando algunos procesos de manera que utilicen más de un procesador.

Esto lo aprendí en una capacitación de Bonita BPM, donde el instructor nos señalaba que la licencia dependerá de la cantidad de procesadores qeu se desee utilizar.

Para Windows:

Los procesos deberán iniciarse con el comando:
start /AFFINITY [n] [archivo ejecutable]
donde [n] es un número decimal equivalente al binario que indica que procesadores se van a utilizar (ya lo explicaré), y [archivo ejecutable] es el ejecutable del programa (sea un .exe, un .bat u otro).


Respecto al valor de n:

Supongamos que tenemos 4 procesadores, entonces de izquierda a derecha los tendríamos:
CPU3 CPU2 CPU1 CPU0
1 para encendido y 0 para apagado. Entonces si por ejemplo queremos usar los 2 primeros procesadores (CPU0 y CPU1): 0011 binario cuyo equivalente en decimal es 3.

Para Linux:

De manera equivalente, los procesos deberán ejecutarse con el comando:
taskset [COREMASK] [EXECUTABLE]
donde [COREMASK] es el hexadecimal equivalente al binario que indica que procesadores se van a utilizar, y [EXECUTABLE] como se podrán imaginar, es precisamente el ejecutable del programa (.sh, binario u otro).

La única salvedad es que [COREMASK] se pone en notación hexadecimal, por ejemplo 3 en decimal es 0x3 en hexadecimal.

Para el tema del [n] y del [COREMASK] este enlace les puede servir: http://www.vlsm-calc.net/decbinhex.php
 

Fuente:
Si me preguntan, la verdad es que ignoro que efectos adversos pueda tener ejecutar  un proceso de esta manera. Pero nunca está de más saber como hacerlo.

viernes, noviembre 08, 2013

Clonando máquinas virtuales Linux

Una tarea relativamente habitual cuando se trabaja con entornos de desarrollo virtualizados es tener que agregar máquinas adicionales. Y para "ahorrarnos trabajo" (créanme que es sólo una falsa ilusión), utilizamos como "base" alguna otra máquina que ya tenemos creada y funcionando.
El problema: Hay alguna cosas que no se nos pueden olvidar, sino lo más probable es que nada funcione como esperamos.

Identificación de la máquina

Típicamente vamos a querer que cada nueva máquina tenga un nombre a lo menos descriptivo, que sirva para saber en que proyecto la tenemos.
En clones de máquinas Linux de sabor CentOS, o Red Hat (o similares), el nombre de la máquina está en el archivo /etc/sysconfig/network

En clones de máquinas Linux de sabor Debian, o Ubuntu (o similares), el nombre de la máquina está en el archivo /etc/hostname, pero además será necesario cambiar la referencia en el archivo /etc/hosts de modo que el IP 127.0.1.1 apunte al nuevo nombre.

Para que los cambios sean efectivos será necesario reiniciar la máquina virtual.

Configuración de red

Por una práctica más bien propia (mucho dirán que es de viejo mañoso), me gusta asignarle IPs fijos a las máquinas. Por ello al clonarlas reinicio la MAC de cada máquina virtual (lo que genera una MAC aleatoria), y utilizo una configuración de 2 tarjetas de red, una NAT y la otra virtual.

Lo primero será borrar el archivo /etc/udev/rules.d/70-persistent-net.rules que hace referencia a la MAC de la tarjeta de red de la máquina clonada (original).

En clones de máquinas Linux de sabor CentOS, o Red Hat (o similares),lo siguiente es editar los archivos /etc/sysconfig/networking/devices/ifcfg-eth*  borrando las líneas que hacen referencia a la MAC y la UUID.

Luego deberán reiniciar y sus máquinas virtuales estarán listas para jugar.

Si alguien tiene algún dato adicional o alguna corrección que agregar, pueden hacerlo en los comentarios.