tag:blogger.com,1999:blog-78902692024-03-07T14:14:36.601-06:00Miguel Angel Huerta GonzalezDe nobis fabula narraturAnonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.comBlogger155125tag:blogger.com,1999:blog-7890269.post-35547595412340794842017-06-23T18:17:00.000-05:002017-06-23T18:17:26.160-05:00jenkins approval, como hacer la lista de approvers de manera dinámica.Algunas veces en el proceso de despliegue se necesita que alguien de un visto bueno o que alguien apruebe de forma manual un despliegue, aunque no es conveniente para continous delivery algunas reglas de negocio son así, pare esto jenkins dispone del plugin <a href="https://wiki.jenkins-ci.org/display/JENKINS/Promoted+Builds+Plugin">Promoted builds</a> que nos permite configurar quien de forma manual una aprobación.<br />
<br />
Se puede leer un buen caso de uso en este <a href="https://www.cloudbees.com/blog/another-look-jenkins-promoted-builds-plugin">blog de cloudbees</a>, empecemos a configurarlo para ver la problemática de este caso. Para activar la promoción se debe de activar la casilla "Promote builds when..."<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj1AkHMeYh3w-4UHnKmbMf9qZT9xJI6gzCOWyiSRFIG00cgZjn4vTkbosZpT8Loo1ATDEURan_pyiFSq4zJwWwxgZHSTnoIBqqT0q68Y796FXhn5BfmclKLaA-QD1hahfmQki1VA/s1600/Screenshot+from+2017-06-22+19-13-56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="500" data-original-width="722" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj1AkHMeYh3w-4UHnKmbMf9qZT9xJI6gzCOWyiSRFIG00cgZjn4vTkbosZpT8Loo1ATDEURan_pyiFSq4zJwWwxgZHSTnoIBqqT0q68Y796FXhn5BfmclKLaA-QD1hahfmQki1VA/s1600/Screenshot+from+2017-06-22+19-13-56.png" /></a></div>
<br />
En este ejemplo una vez que se ejecute el job de jenkins la promoción manual solo pintará en el log "despliegue" y solo lo podrá aprobar el usuario admin.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP7KymYeEE6bJlR2Y_cxChHTJOiajjsIPm6U6H5Do3VnIhCrG4Zj1ppiR7iL4o6Tsk5z-AeN6VJ8sxeiJ77WYJl3BPIOMaraedfQto7jIXGv-0JidcikC1CEtSWBWlPBgNylgQpA/s1600/Screenshot+from+2017-06-22+19-21-26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="400" data-original-width="1007" height="254" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP7KymYeEE6bJlR2Y_cxChHTJOiajjsIPm6U6H5Do3VnIhCrG4Zj1ppiR7iL4o6Tsk5z-AeN6VJ8sxeiJ77WYJl3BPIOMaraedfQto7jIXGv-0JidcikC1CEtSWBWlPBgNylgQpA/s640/Screenshot+from+2017-06-22+19-21-26.png" width="640" /></a></div>
<br />
se puede ver el botón approve para el usuario admin y la salida sería:<br />
<br />
<pre style="white-space: pre-wrap; word-wrap: break-word;">Started by user admin
Building in workspace /var/lib/jenkins/workspace/test-approve
Promoting test-approve #1
[test-approve] $ /bin/sh -xe /tmp/jenkins552387212222026236.sh
+ echo despliegue
despliegue
build hudson.tasks.Shell@38223f2c SUCCESS
Finished: SUCCESS</pre>
<br />
<br />
Este plugin no dispone de una manaera de hacer que esta lista sea dinámica mediante variables del tipo $APPROVERS_LIST en el campo de <span style="background-color: rgba(0 , 0 , 0 , 0.024); color: #333333; font-family: "helvetica" , "arial" , sans-serif; font-size: 13px; white-space: nowrap;">Approvers</span> por lo que tenemos que modificar el job cada que se nos pida un nuevo approver.<br />
<br />
Para solucionar esta deficioencia podemos agregar approvers de forma manual agregando el plugin <a href="https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin">Environment injector plugin</a> y activar la promoción automática con un script de groovy.<br />
<br />
Activaremos la casilla "Prepare an environment for the run" y agregaremos la variable de ambiente APPROVERS, o como es mi caso agregué un archivo properties con la configuración de la variable.<br />
<blockquote class="tr_bq">
<br />
/var/lib/jenkins/secrets/approvers.properties<br />
APPROVERS=admin,operator,approver1</blockquote>
<br />
<br />
En nuestra configuración de promoción agregaremos el siguiente script de groovy, el cuál lo que hace es modificar los approvers injectando la lista que se encuentre en APPROVERS<br />
<br />
<br />
<br />
<br />
Y lo más importante es activar "Promote immediately once the build is complete" para que se ejecute el archivo de groovy una vez que nuestro job a sido costruido.<br />
<br />
Ahora una vez que se construye el proyecto la lista de approvers debe de cambiar:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrVRMfOQVpuHnRxraoYJzIv9I3G_8Rjzh3-FTKr2FUqMJui7SaCtc4TjzeOFvEFsAFfk87sHYWUSsd3KbDxIBn5lY1HWNJbb-ngwR77o9WvagWeLBPJ7F4BVOU2KNCZlEZCRlfKw/s1600/Screenshot+from+2017-06-22+19-41-58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="429" data-original-width="998" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrVRMfOQVpuHnRxraoYJzIv9I3G_8Rjzh3-FTKr2FUqMJui7SaCtc4TjzeOFvEFsAFfk87sHYWUSsd3KbDxIBn5lY1HWNJbb-ngwR77o9WvagWeLBPJ7F4BVOU2KNCZlEZCRlfKw/s640/Screenshot+from+2017-06-22+19-41-58.png" width="640" /></a></div>
<br />
Dependiendo de la versión de jenkins tendrán que aprobar el uso del script de groovy, se hace en <jenkins_url>/scriptApproval/<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2FTblW64CHZQetW5vn91YFBBP3eElpCWb6wH9q-kkLSqhb0aHD0l3f_RVXEYCOOhsIG-SE2UzH4x3AXT_hSPCF4X3CoXJ2NzR4erAhHqP8s5R8DFoacPZrJPrFkyXuJLtQc4s8g/s1600/Screenshot+from+2017-06-22+20-08-40.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="176" data-original-width="975" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2FTblW64CHZQetW5vn91YFBBP3eElpCWb6wH9q-kkLSqhb0aHD0l3f_RVXEYCOOhsIG-SE2UzH4x3AXT_hSPCF4X3CoXJ2NzR4erAhHqP8s5R8DFoacPZrJPrFkyXuJLtQc4s8g/s640/Screenshot+from+2017-06-22+20-08-40.png" width="640" /></a></div>
<br />
<br />
Por alguna razón siempre hay que borrar la opción de classpath del plugin de groovy, sino jenkins se enojará mucho y nos lanzará una exception.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1BbR2pJYlIMtlO7HL2DTeIvFzJuMpD69Cr0UumLHmXPGWQ-u4yrtZ4WT-4K0yPGDOpLg2eqJPPWr2cD0RoawJLXEWhuOBGjsYvjWbDtZV74b4kr1jmjOiAr1kLB7opqX5zTBK6Q/s1600/Screenshot+from+2017-06-22+20-10-01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="361" data-original-width="999" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1BbR2pJYlIMtlO7HL2DTeIvFzJuMpD69Cr0UumLHmXPGWQ-u4yrtZ4WT-4K0yPGDOpLg2eqJPPWr2cD0RoawJLXEWhuOBGjsYvjWbDtZV74b4kr1jmjOiAr1kLB7opqX5zTBK6Q/s320/Screenshot+from+2017-06-22+20-10-01.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLKSTEAW8iSv0m_GJJqxRm0R_kBjT_3WUkP3y2RAd39jHb3Jg8ODiJOWVASJ45qA2ore8kZ8ARx3Olte5oms4pdA68aweOYtwFn4qwXPMgFwlNx9Fg4eGPIE5zDKEUJmi2nUHMLg/s1600/Screenshot+from+2017-06-22+20-06-01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="449" data-original-width="895" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLKSTEAW8iSv0m_GJJqxRm0R_kBjT_3WUkP3y2RAd39jHb3Jg8ODiJOWVASJ45qA2ore8kZ8ARx3Olte5oms4pdA68aweOYtwFn4qwXPMgFwlNx9Fg4eGPIE5zDKEUJmi2nUHMLg/s320/Screenshot+from+2017-06-22+20-06-01.png" width="320" /></a></div>
<br />
<br />
<br />
<br />
Dejo un caso de ejemplo en este repo, <a href="https://github.com/hgmiguel/ansible-kitchen-test">ansible-test-kitchen</a> bastaría con ejecutarlo asi:<br />
<blockquote class="tr_bq">
(master *)$ bundle exec kitchen converge default-ubuntu-promoted-builds-jenkins </blockquote>
<br />
Y un pequeño video sobre este tema:<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/JvuNhECM0QU" width="560"></iframe>
<br />Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-92119665464104761442016-11-03T14:45:00.000-06:002016-11-03T14:45:32.294-06:00Sobre wildfly y jboss-cliHace mucho que no trabajaba con jboss y en esta ocasión me tocó ocupar su versión gratuita <a href="http://wildfly.org/">Wildfly</a>,<br />
<br />
Wildlfy y jboss tienen una<a href="https://developer.jboss.org/wiki/CommandLineInterface"> consola </a>con la cuál se puede hacer scripting, sin más el siguiente script lo ocupé para generar un nuevo servidor por cada ambiente que era necesario crear desde jenkins<br />
<br />
Ver el siguiente gist:
<br />
<script src="https://gist.github.com/hgmiguel/104e905a750e1e8e49910843f32b368e.js"></script>
<br />
<br />
Solo falta agregar unas<a href="https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Build"> variables de ambiente</a> y correr el job con la opción "build with parameters"<br />
<br />Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-7230736866452295692016-06-01T15:52:00.000-05:002016-06-01T20:39:18.274-05:00Autoscalar RDS postgresql, más o menos.<br>
<br>
Una de las cosas más difíciles de auto escalar son las bases de datos, aws amazon nos da la opción de escalar horizontalmente una base de datos mediante read replicas, lamentablemente en el caso de postgresql solo nos deja crear 5 y no hay opción de hacer read replicas de las read replicas (Esto se puede hacer en mysql).<br>
<br>
Una forma de seguir creciendo es promover una read replica a master y crear sus otras 5 read replicas, pero hay que implementar un mecanismo de sincronización entre las dos master.<br>
<br>
La otra es empezar con pocas read replicas he irlas creando de acuerdo a la demanda, esta opción es buena pero no es rápida, se basaría más en predecir el tráfico antes de que llegue, lo malo es que crear una read replica es lento aproximadamente unos 20 min.<br>
<br>
Un escenario es tener creadas las 5 read replicas y escalarlas verticalmente cambiando el tipo de clase de instancias. Esta solución también es lenta, unos 7 u 8 min. Una ventaja de esta solución es que podemos jugar con los costos y por ejemplo tener solo 2 read replcias grandes y otras 3 micro.<br>
<br>
Para que esta opción funcione necesitamos una arquitectura parecida a esta:<br>
<br>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1Rwgv3zfHavd52d6f-neEzP47XQjDVK-V2_TB9DRb-IiyocVZaOrNN7BGocANwPJysKuSAqbB9UHlLo-KE0fwuQC4Un2LodmL4dMW6MCSglVuQCN2T4Rb6th3rukF3PfO0BTJjQ/s1600/Untitled+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1Rwgv3zfHavd52d6f-neEzP47XQjDVK-V2_TB9DRb-IiyocVZaOrNN7BGocANwPJysKuSAqbB9UHlLo-KE0fwuQC4Un2LodmL4dMW6MCSglVuQCN2T4Rb6th3rukF3PfO0BTJjQ/s640/Untitled+%25281%2529.png" width="608"></a></div>
<br>
<br>
En nuestro ejemplo necesitaremos lo siguiente:<br>
1. Tener creado un rds master con una read replica<br>
2. Tener configurado 2 instancias con pgboucer uno para apuntar a master y otro a readreplica<br>
3. Tener configurado un loadbalancer con esas dos instancias.<br>
<br>
<div style="text-align: left;">
La modificación de las clase y encender el pgbouncer lo realizaremos a través de lambdas, las lambdas que usaremos son <a href="https://gist.github.com/hgmiguel/785c81e71de1fdd67fb489bfdda2c115#file-upgrade_db_instance_class-py">upgrade_db_instance_class</a> y <a href="https://gist.github.com/hgmiguel/785c81e71de1fdd67fb489bfdda2c115#file-add_read_replica_to_elb-py">add_read_replica_to_elb</a>.<br>
<br></div>
<div style="text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWMeZUOJvOgo2fC0P_8BKT3ZNTacdTSPib_3Sz8jIpCFh5CqDZI3p1WNhS2PiQ8fU10YITF1j-Zj8mB0H9UkKVt6WM1BLqOFp6MmcLOIRbi9gU267MFDrGu4u08dReQvW6gU8oXw/s1600/lambdas.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWMeZUOJvOgo2fC0P_8BKT3ZNTacdTSPib_3Sz8jIpCFh5CqDZI3p1WNhS2PiQ8fU10YITF1j-Zj8mB0H9UkKVt6WM1BLqOFp6MmcLOIRbi9gU267MFDrGu4u08dReQvW6gU8oXw/s640/lambdas.png" width="640"></a></div>
<br>
Necesitaremos crear 2 topicos de SNS, para atacharlos a los eventos, uno de cloudwatch que disparará el aumento de la read replica y otro al finalizar la modificación de la read replica.<br>
<br>
<br>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiriAUPOJthKKmJ9Z0p3MVC447J-O7YJnRcMXIOIOaw_AuQBLv1iHyz3m38aRAMCrPVDxHmNtQyN8KqI_uoMwNEtC11FR0l2bOVZdLNvOFXnsWVoUezfe_Z3qBhz0euNwUahBuzhg/s1600/creat+topico+80+percent.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiriAUPOJthKKmJ9Z0p3MVC447J-O7YJnRcMXIOIOaw_AuQBLv1iHyz3m38aRAMCrPVDxHmNtQyN8KqI_uoMwNEtC11FR0l2bOVZdLNvOFXnsWVoUezfe_Z3qBhz0euNwUahBuzhg/s640/creat+topico+80+percent.png" width="640"></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfyQHBAxXDdD37mA1qjuZbv2FJhwPC4bih-8j802NPNslwQCyrSKV5PyJLr-IGgJisEDnfo441qKpSb13V2gGpZPUoyBOt-xpLlD1OGyrfptbMCQ3WDMTqOzW97DpTkLk1po-7BQ/s1600/12+creacion+de+topicos.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfyQHBAxXDdD37mA1qjuZbv2FJhwPC4bih-8j802NPNslwQCyrSKV5PyJLr-IGgJisEDnfo441qKpSb13V2gGpZPUoyBOt-xpLlD1OGyrfptbMCQ3WDMTqOzW97DpTkLk1po-7BQ/s640/12+creacion+de+topicos.png" width="640"></a></div>
<br>
Y crearemos dos suscripciones, cada uno a su respectivo lambda:<br>
<br>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhroDs2xjwv9EE3EBzyX96M9fAx1IgKotrMUg-qt0M9Q-NVeCUhF3RotdZQu6suUng41IwJVToa73qv5gi4dPsEfFd74dKB8wsrDw_FAwn5gCROlKAlXVIWaysoUKfeoD90utup7Q/s1600/5-subscribe-add-replica.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhroDs2xjwv9EE3EBzyX96M9fAx1IgKotrMUg-qt0M9Q-NVeCUhF3RotdZQu6suUng41IwJVToa73qv5gi4dPsEfFd74dKB8wsrDw_FAwn5gCROlKAlXVIWaysoUKfeoD90utup7Q/s640/5-subscribe-add-replica.png" width="640"></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt4RDny_JyGeO6cgeITMwK7Ewwpd3FDirrot343-_3TU8xztDVlXQQnH076eUAKRI86nCyuD3zK4esDI6QPRRD-kv54zwdtnvv9qxj-_Kd-nbRKqIOpCxKYwjtuQSWcGYyWpv3zw/s1600/4-subscribe-80.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt4RDny_JyGeO6cgeITMwK7Ewwpd3FDirrot343-_3TU8xztDVlXQQnH076eUAKRI86nCyuD3zK4esDI6QPRRD-kv54zwdtnvv9qxj-_Kd-nbRKqIOpCxKYwjtuQSWcGYyWpv3zw/s640/4-subscribe-80.png" width="640"></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br></div>
<div class="separator" style="clear: both; text-align: left;">
Crearemos una alerta y mandaremos la notificación al topico 80_percent_rds</div>
<div class="separator" style="clear: both; text-align: left;">
<br></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLn71h7Rpf62orUl5FJuv2DYEzp1xDIyAz60Dbw0CHR8rH3EU1EhJ9HDMgkYcTHqA4nnJUxoMiCp5f_lAUKr-xZaNHcWPy48OV1_uKSsEFmlEcUVPu_E_NAqWw4WoJd5xdA4QXVQ/s1600/6-cloudwatch-alarm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="412" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLn71h7Rpf62orUl5FJuv2DYEzp1xDIyAz60Dbw0CHR8rH3EU1EhJ9HDMgkYcTHqA4nnJUxoMiCp5f_lAUKr-xZaNHcWPy48OV1_uKSsEFmlEcUVPu_E_NAqWw4WoJd5xdA4QXVQ/s640/6-cloudwatch-alarm.png" width="640"></a></div>
<br>
Y crearemos un Event Suscriptions en el rds para apuntarlo al topico upgrade_rds<br>
<br>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCq62xTC5Yzvh1WHR4k5Xrepe4hMppii-E6NLl3YH1ll4Vt6GhqWT6MUTX7oceUgtkf_-CbGuP3w2J-oYv9RhY2vg4q2zUHxdOCjATKliCZkjzLH7VjUOfnQxfL3p45LKaThcoZg/s1600/8-create-event-suscription.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCq62xTC5Yzvh1WHR4k5Xrepe4hMppii-E6NLl3YH1ll4Vt6GhqWT6MUTX7oceUgtkf_-CbGuP3w2J-oYv9RhY2vg4q2zUHxdOCjATKliCZkjzLH7VjUOfnQxfL3p45LKaThcoZg/s640/8-create-event-suscription.png" width="640"></a></div>
<br>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br>
<br>
Habiendo hecho todo eso ya podemos empezar con nuestra prueba.<br>
<br>
<br>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/wzrqiW5FqGw/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/wzrqiW5FqGw?feature=player_embedded" width="320"></iframe></div>
<br>
<br>
<br>
<br>
Ocuparemos una instancia para lanzar las pruebas, estas se harán con pgbench y lo podemos instalar de la siguiente manera.<br>
<br>
<blockquote class="tr_bq">
$ sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list'<br>
$ wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | sudo apt-key add -<br>
$ sudo apt-get update<br>
$ sudo apt-get install postgresql-contrib</blockquote>
<br>
Ahora meteremos datos a la instancia:<br>
<blockquote class="tr_bq">
$ pgbench -h hgmigueldb.comvkfys9x22.us-east-1.rds.amazonaws.com -p 5432 -Uhgmiguel -i -s 300 hgmigueldb</blockquote>
<br>
Y así correremos las pruebas:<br>
<blockquote class="tr_bq">
$pgbench -h internal-pgbouncer-elb-592002074.us-east-1.elb.amazonaws.com -p 6432 -Uhgmiguel -c 64 -j 4 -T 900 -C -S hgmigueldb</blockquote>
<br>
Como ven ocupamos muchas cosas del entorno de aws, SNS, Lambdas, RDS, ELB, y Cloudwatch para poder armar esta arquitectura. Llega a ser abrumante la cantidad de servicios que hay en amazon pero conocerlos todos nos ayuda a crear mejores soluciones.Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-49034901561283750742014-11-11T09:12:00.000-06:002014-11-11T09:12:04.845-06:00groovy dinámicoLos<a href="http://groovy.codehaus.org/Closures"> closures en groovy </a>son muy utilizados, pero transformar metodos a closures no tanto, hay muchas ventajas como poder pasarlos como parametros de funciones o como valores en un mapa, para esto se ocupa el operador (.&)<br />
<br />
<pre id="vimCodeElement"><span class="LineNr" id="L13">13 </span> <span class="Special">def</span> dynamicMethods = <span class="level15c">[</span><span class="level14c">[</span>regex:<span class="Constant">"hasPermission(.*)"</span>, method:<span class="Type">this</span><span style="background-color: yellow;">.&hasPermission</span><span class="level14c">]</span>,
<span class="LineNr" id="L14">14 </span> <span class="level14c">[</span>regex:<span class="Constant">"create(.*)Permission"</span>, method:<span class="Type">this</span><span style="background-color: yellow;">.&createPermission</span><span class="level14c">]</span><span class="level15c">]</span>
</pre>
<br />
<br />
En este caso estamos convirtiendo el método hasPermission() y createPermission() en un closure y pasandoselo a un mapa, el metodo puede ser llamado como se muestra en la línea 21.<br />
<br />
<pre id="vimCodeElement"><span class="LineNr" id="L17">17 </span> <span class="Special">def</span> methodMissing<span class="level15c">(</span><span class="Type">String</span> name, args<span class="level15c">)</span> <span class="level15c">{</span>
<span class="LineNr" id="L18">18 </span> | <span class="Special">def</span> method = dynamicMethods.<span class="Identifier">find</span> <span class="level14c">{</span> name =~ it.regex<span class="level14c">}</span>
<span class="LineNr" id="L19">19 </span> | <span class="Statement">if</span><span class="level14c">(</span>method<span class="level14c">)</span> <span class="level14c">{</span>
<span class="LineNr" id="L20">20 </span> | | <span class="Special">def</span> match = name =~ method.regex
<span class="LineNr" id="L21">21 </span> | | <span class="Statement">return</span> <span style="background-color: yellow;">method.method<span class="level13c">(</span>match<span class="level12c">[</span><span class="Constant">0</span><span class="level12c">][</span><span class="Constant">1</span><span class="level12c">]</span>, * args<span class="level13c">)</span></span>
<span class="LineNr" id="L22">22 </span> | <span class="level14c">}</span> <span class="Statement">else</span>
<span class="LineNr" id="L23">23 </span> | | <span class="Statement">throw</span> <span class="Statement">new</span> <span class="Statement">MissingMethodException</span><span class="level14c">(</span>name, DemoDynamic , args<span class="level14c">)</span>
<span class="LineNr" id="L24">24 </span> <span class="level15c">}</span>
<span class="LineNr" id="L25">25 </span>
<span class="LineNr" id="L26">26 </span><span class="level16c">}</span>
</pre>
<br />
Ocupando una expresión regular y <a href="http://groovy.codehaus.org/Using+methodMissing+and+propertyMissing">methodMissing</a> podemos mandar a llamar dinámicamente el método que vive en el mapa.<br />
<br />
<pre id="vimCodeElement"><span class="LineNr" id="L32">32 </span><span class="Statement">assert</span> demo.hasPermission<span style="background-color: yellow;">Hgmiguel</span><span class="level16c">(</span><span class="Constant">"write"</span><span class="level16c">)</span>
<span class="LineNr" id="L33">33 </span><span class="Statement">assert</span> !demo.hasPermission<span style="background-color: yellow;">Otros</span><span class="level16c">(</span><span class="Constant">"write"</span><span class="level16c">)</span>
</pre>
<br />
Dejo un Gist como ejemplo de lo que quiero explicar:
<br />
<div class="gistLoad" data-id="6626cb0162715d0eaa71" id="gist-6626cb0162715d0eaa71">
<script src="https://gist.github.com/hgmiguel/6626cb0162715d0eaa71.js"></script>
</div>
Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-7268541201961117132014-05-26T11:30:00.000-05:002014-05-26T12:02:01.071-05:00Eliminar switch<pre style="padding: 0px;">Hace mucho que no programo en java o algo parecido, últimamente hago mucho <a href="http://groovy.codehaus.org/">groovy</a> y estoy aprendiendo <a href="http://clojure.org/">clojure</a>, pero bueno, revisando código en el trabajo me topé con una estructura switch, y de inmediato llamo mi atención:<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"></pre>
<span style="color: #333333; font-family: Bitstream Vera Sans Mono, DejaVu Sans Mono, Monaco, monospace;"><span style="background-color: white; font-size: 12px; line-height: 1.4000000000000001;">
</span></span><pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"> <span class="n">List</span><span class="o"><</span><span class="n">Role</span><span class="o">></span> <span class="n">findInviteRoles</span><span class="o">()</span> <span class="o">{</span>
<a href="https://www.blogger.com/null" name="cl-22" style="color: #3b73af;"></a> <span class="n">List</span> <span class="n">roles</span> <span class="o">=</span> <span class="o">[]</span>
<a href="https://www.blogger.com/null" name="cl-23" style="color: #3b73af;"></a> <span class="kt" style="color: #445588;">def</span> <span class="n">authorities</span> <span class="o">=</span> <span class="n">springSecurityService</span><span class="o">.</span><span class="na" style="color: teal;">getAuthentication</span><span class="o">().</span><span class="na" style="color: teal;">authorities</span>
<a href="https://www.blogger.com/null" name="cl-24" style="color: #3b73af;"></a> <span class="n">authorities</span><span class="o">.</span><span class="na" style="color: teal;">each</span> <span class="o">{</span><span class="n">role</span> <span class="o">-></span>
<a href="https://www.blogger.com/null" name="cl-25" style="color: #3b73af;"></a> <span class="k" style="color: #004080;">switch</span> <span class="o">(</span><span class="n">role</span><span class="o">){</span>
<a href="https://www.blogger.com/null" name="cl-26" style="color: #3b73af;"></a> <span class="k" style="color: #004080;">case</span> <span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">:</span>
<a href="https://www.blogger.com/null" name="cl-27" style="color: #3b73af;"></a> <span class="n">roles</span> <span class="o">+=</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">]</span>
<a href="https://www.blogger.com/null" name="cl-28" style="color: #3b73af;"></a> <span class="k" style="color: #004080;">break</span>
<a href="https://www.blogger.com/null" name="cl-29" style="color: #3b73af;"></a> <span class="k" style="color: #004080;">case</span> <span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">:</span>
<a href="https://www.blogger.com/null" name="cl-30" style="color: #3b73af;"></a> <span class="n">roles</span> <span class="o">+=</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">]</span>
<a href="https://www.blogger.com/null" name="cl-31" style="color: #3b73af;"></a> <span class="k" style="color: #004080;">break</span>
<a href="https://www.blogger.com/null" name="cl-32" style="color: #3b73af;"></a> <span class="k" style="color: #004080;">case</span> <span class="s1" style="color: #bb8844;">'ROLE_1'</span><span class="o">:</span>
<a href="https://www.blogger.com/null" name="cl-33" style="color: #3b73af;"></a> <span class="n">roles</span> <span class="o">+=</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_2'</span><span class="o">]</span>
<a href="https://www.blogger.com/null" name="cl-34" style="color: #3b73af;"></a> <span class="k" style="color: #004080;">break</span>
<a href="https://www.blogger.com/null" name="cl-35" style="color: #3b73af;"></a> <span class="o">}</span>
<a href="https://www.blogger.com/null" name="cl-36" style="color: #3b73af;"></a> <span class="o">}</span>
<a href="https://www.blogger.com/null" name="cl-37" style="color: #3b73af;"></a> <span class="n">Role</span><span class="o">.</span><span class="na" style="color: teal;">where</span> <span class="o">{</span>
<a href="https://www.blogger.com/null" name="cl-38" style="color: #3b73af;"></a> <span class="n">authority</span> <span class="k" style="color: #004080;">in</span> <span class="n">roles</span><span class="o">.</span><span class="na" style="color: teal;">unique</span><span class="o">()</span>
<a href="https://www.blogger.com/null" name="cl-39" style="color: #3b73af;"></a> <span class="o">}.</span><span class="na" style="color: teal;">list</span><span class="o">()</span>
<a href="https://www.blogger.com/null" name="cl-40" style="color: #3b73af;"></a> <span class="o">}</span></pre>
</pre>
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"></pre>
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"></pre>
A lo cual me pareció que el switch iba sobrado y decidimos hacer un refactor. Se puede ver que ahora están separados los datos de la lógica, lo cuál en algunos casos facilita la mantenibilidad del código.<br />
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"></pre>
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"> <span class="n">List</span><span class="o"><</span><span class="n">Role</span><span class="o">></span> <span class="n">findInviteRoles</span><span class="o">()</span> <span class="o">{</span>
<a href="https://www.blogger.com/null" name="cl-22" style="color: #3b73af;"></a> <span class="n">List</span> <span class="n">roles</span> <span class="o">=</span> <span class="o">[]</span>
<a href="https://www.blogger.com/null" name="cl-23" style="color: #3b73af;"></a> <span class="kt" style="color: #445588;">def</span> <span class="n">permisos</span> <span class="o">=</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">:[</span><span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">],</span> </pre>
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"><span class="s1" style="color: #bb8844;"> 'ROLE_ADMIN'</span><span class="o">:[</span><span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">],</span> </pre>
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"><span class="s1" style="color: #bb8844;"> 'ROLE_1'</span><span class="o">:[</span><span class="s1" style="color: #bb8844;">'ROLE_2'</span><span class="o">]]</span>
<a href="https://www.blogger.com/null" name="cl-24" style="color: #3b73af;"></a> <span class="kt" style="color: #445588;">def</span> <span class="n">authorities</span> <span class="o">=</span> <span class="n">springSecurityService</span><span class="o">.</span><span class="na" style="color: teal;">getAuthentication</span><span class="o">().</span><span class="na" style="color: teal;">authorities</span>
<a href="https://www.blogger.com/null" name="cl-25" style="color: #3b73af;"></a> <span class="n">roles</span> <span class="o">=</span> <span class="n">authorities</span><span class="o">.</span><span class="na" style="color: teal;">collect</span> <span class="o">{</span>
<a href="https://www.blogger.com/null" name="cl-26" style="color: #3b73af;"></a> <span class="n">permisos</span><span class="o">.</span><span class="s2" style="color: #bb8844;">"${it}"</span>
<a href="https://www.blogger.com/null" name="cl-27" style="color: #3b73af;"></a> <span class="o">}.</span><span class="na" style="color: teal;">flatten</span><span class="o">().</span><span class="na" style="color: teal;">unique</span><span class="o">()</span>
<a href="https://www.blogger.com/null" name="cl-28" style="color: #3b73af;"></a> <span class="n">Role</span><span class="o">.</span><span class="na" style="color: teal;">where</span> <span class="o">{</span>
<a href="https://www.blogger.com/null" name="cl-29" style="color: #3b73af;"></a> <span class="n">authority</span> <span class="k" style="color: #004080;">in</span> <span class="n">roles</span>
<a href="https://www.blogger.com/null" name="cl-30" style="color: #3b73af;"></a> <span class="o">}.</span><span class="na" style="color: teal;">list</span><span class="o">()</span>
<a href="https://www.blogger.com/null" name="cl-31" style="color: #3b73af;"></a> <span class="o">}</span></pre>
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"><span class="o">
</span></pre>
A mi punto de vista queda más limpio y claro el objetivo del <a href="http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#collect(groovy.lang.Closure)">collect</a> que del switch. Dejo la prueba unitaria.<br />
<pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4000000000000001; padding: 0px;"><pre style="font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; line-height: 1.4000000000000001; padding: 0px;"> <span class="nd">@Unroll</span>
<a href="https://www.blogger.com/null" name="cl-44" style="color: #3b73af;"></a> <span class="kt" style="color: #445588;">def</span> <span class="s2" style="color: #bb8844;">"test list granted invite roles with #userRole and result #listResult"</span><span class="o">()</span> <span class="o">{</span>
<a href="https://www.blogger.com/null" name="cl-45" style="color: #3b73af;"></a> <span class="nl">setup:</span> <span class="s2" style="color: #bb8844;">"mockering"</span>
<a href="https://www.blogger.com/null" name="cl-46" style="color: #3b73af;"></a> <span class="n">SpringSecurityService</span><span class="o">.</span><span class="na" style="color: teal;">metaClass</span><span class="o">.</span><span class="na" style="color: teal;">getAuthentication</span> <span class="o">=</span> <span class="o">{</span> <span class="o">[</span><span class="nl">authorities:</span> <span class="o">[</span><span class="n">userRole</span><span class="o">]]</span> <span class="o">}</span>
<a href="https://www.blogger.com/null" name="cl-47" style="color: #3b73af;"></a> <span class="kt" style="color: #445588;">def</span> <span class="n">springSecurityService</span> <span class="o">=</span> <span class="n">Mock</span><span class="o">(</span><span class="n">SpringSecurityService</span><span class="o">)</span>
<a href="https://www.blogger.com/null" name="cl-48" style="color: #3b73af;"></a> <span class="n">service</span><span class="o">.</span><span class="na" style="color: teal;">springSecurityService</span> <span class="o">=</span> <span class="n">springSecurityService</span>
<a href="https://www.blogger.com/null" name="cl-49" style="color: #3b73af;"></a>
<a href="https://www.blogger.com/null" name="cl-50" style="color: #3b73af;"></a> <span class="n">systemRoles</span><span class="o">.</span><span class="na" style="color: teal;">each</span> <span class="o">{</span>
<a href="https://www.blogger.com/null" name="cl-51" style="color: #3b73af;"></a> <span class="kt" style="color: #445588;">def</span> <span class="n">role</span> <span class="o">=</span> <span class="k" style="color: #004080;">new</span> <span class="n">Role</span><span class="o">(</span><span class="nl">authority:</span> <span class="n">it</span><span class="o">)</span>
<a href="https://www.blogger.com/null" name="cl-52" style="color: #3b73af;"></a> <span class="n">role</span><span class="o">.</span><span class="na" style="color: teal;">save</span> <span class="nl">validate:</span> <span class="kc" style="color: #004080;">false</span>
<a href="https://www.blogger.com/null" name="cl-53" style="color: #3b73af;"></a> <span class="o">}</span>
<a href="https://www.blogger.com/null" name="cl-54" style="color: #3b73af;"></a>
<a href="https://www.blogger.com/null" name="cl-55" style="color: #3b73af;"></a> <span class="nl">when:</span>
<a href="https://www.blogger.com/null" name="cl-56" style="color: #3b73af;"></a> <span class="kt" style="color: #445588;">def</span> <span class="n">list</span> <span class="o">=</span> <span class="n">service</span><span class="o">.</span><span class="na" style="color: teal;">findInviteRoles</span><span class="o">()</span>
<a href="https://www.blogger.com/null" name="cl-57" style="color: #3b73af;"></a>
<a href="https://www.blogger.com/null" name="cl-58" style="color: #3b73af;"></a> <span class="nl">then:</span>
<a href="https://www.blogger.com/null" name="cl-59" style="color: #3b73af;"></a> <span class="n">list</span><span class="o">?.</span><span class="na" style="color: teal;">authority</span> <span class="o">==</span> <span class="n">listResult</span>
<a href="https://www.blogger.com/null" name="cl-60" style="color: #3b73af;"></a>
<a href="https://www.blogger.com/null" name="cl-61" style="color: #3b73af;"></a> <span class="nl">where:</span>
<a href="https://www.blogger.com/null" name="cl-62" style="color: #3b73af;"></a> <span class="n">userRole</span> <span class="o">|</span> <span class="n">systemRoles</span> <span class="o">||</span> <span class="n">listResult</span>
<a href="https://www.blogger.com/null" name="cl-63" style="color: #3b73af;"></a> <span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span> <span class="o">|</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_GARBAGE'</span><span class="o">]</span> <span class="o">||</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">]</span>
<a href="https://www.blogger.com/null" name="cl-64" style="color: #3b73af;"></a> <span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span> <span class="o">|</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_GARBAGE'</span><span class="o">]</span> <span class="o">||</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">]</span>
<a href="https://www.blogger.com/null" name="cl-65" style="color: #3b73af;"></a> <span class="s1" style="color: #bb8844;">'ROLE_1'</span> <span class="o">|</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_GARBAGE'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_2'</span><span class="o">]</span> <span class="o">||</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_2'</span><span class="o">]</span>
<a href="https://www.blogger.com/null" name="cl-66" style="color: #3b73af;"></a> <span class="s1" style="color: #bb8844;">'ROLE_2'</span> <span class="o">|</span> <span class="o">[</span><span class="s1" style="color: #bb8844;">'ROLE_ADMIN'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_ROOT'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_GARBAGE'</span><span class="o">,</span> <span class="s1" style="color: #bb8844;">'ROLE_2'</span><span class="o">]</span> <span class="o">||</span> <span class="o">[]</span>
<a href="https://www.blogger.com/null" name="cl-67" style="color: #3b73af;"></a> <span class="o">}</span></pre>
</pre>
Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-82036673470401877132014-03-19T23:23:00.000-06:002014-03-20T11:24:47.187-06:00Métrica C.R.A.P. con grails<br />
Qué es el anti patrón de riezgo de cambio (Change Risk Anti-Patterns C.R.A.P)?<br />
es un método para analizar y predecir la cantidad de esfuerzo, trabajo y tiempo requerido para mantener la base de código existente. La fórmula de C.R.A.P. es la siguiente:<br />
<br />
C.R.A.P.(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)<br />
<br />
Donde comp(m) es la<a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity"> complejidad ciclomática </a>del método m y cov(m) es la cobertura de pruebas del método m.<br />
<br />
Más información <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=210575">aqui</a>:<br />
<br />
Al intentar configurar CRAP para grails me topé con que no pude utilizar <a href="http://gmetrics.sourceforge.net/gmetrics-CrapMetric.html">gmetrics</a>, le di muchas vueltas hasta que lo abandoné, leyendo sobre métricas me topé con esta <a href="http://slidedeck.io/beckje01/gr8us-2013-static-talk">presentación</a> y ahí me di cuenta de que <a href="http://codenarc.sourceforge.net/codenarc-rules-size.html#CrapMetric">codenarc ya contiene a CRAP entre sus reglas</a>. He aquí como lo configuré:<br />
<br />
<br />
<br />
<br />
buildconfig.groovy<br />
<pre style="background-color: white;"><pre><span class="lnr" style="color: #804000;"> 25 </span>grails.project.dependency.resolution = {
<span class="lnr" style="color: #804000;"> 54 </span>
<span class="lnr" style="color: #804000;"> 55 </span> dependencies {
<span class="lnr" style="color: #804000;"> 66 </span> compile <span class="Constant" style="color: #c00000;">'org.gmetrics:GMetrics:0.6'</span>
<span class="lnr" style="color: #804000;"> 69 </span> }
<span class="lnr" style="color: #804000;"> 70 </span>
<span class="lnr" style="color: #804000;"> 71 </span> plugins {
<span class="lnr" style="color: #804000;"> 85 </span> compile <span class="Constant" style="color: #c00000;">':codenarc:0.20'</span>
<span class="lnr" style="color: #804000;"> 99 </span> }
<span class="lnr" style="color: #804000;">100 </span>}
<span class="lnr" style="color: #804000;">101 </span>
<span class="lnr" style="color: #804000;">110 </span>
<span class="lnr" style="color: #804000;">111 </span>codenarc.reports = {
<span class="lnr" style="color: #804000;">116 </span> MyHtmlReport(<span class="Constant" style="color: #c00000;">'html'</span>) {
<span class="lnr" style="color: #804000;">117 </span> outputFile = <span class="Constant" style="color: #c00000;">'target/CodeNarcReport.html'</span>
<span class="lnr" style="color: #804000;">118 </span> title = <span class="Constant" style="color: #c00000;">'Violations Report'</span>
<span class="lnr" style="color: #804000;">119 </span> }
<span class="lnr" style="color: #804000;">120 </span>}
<span class="lnr" style="color: #804000;">121 </span>
<span class="lnr" style="color: #804000;">122 </span>codenarc.propertiesFile=<span class="Constant" style="color: #c00000;">'grails-app/conf/codenarc.properties'</span>
<span class="lnr" style="color: #804000;">123 </span>codenarc.ruleSetFiles = <span class="Constant" style="color: #c00000;">"rulesets/basic.xml,rulesets/exceptions.xml, rulesets/imports.xml,rulesets/grails.xml, rulesets/unused.xml, rulesets/size.xml, rulesets/concurrency.xml,rulesets/convention.xml,rulesets/design.xml,rulesets/groovyism.xml,rulesets/imports.xml,rulesets/logging.xml"</span>
<span class="lnr" style="color: #804000;">124 </span></pre>
</pre>
<br />
codenarc.properties<br />
<pre style="background-color: white;"><span class="lnr" style="color: #804000;"> 1 </span><span class="Identifier" style="color: teal;">GrailsPublicControllerMethod.enabled</span>=<span class="Constant" style="color: #c00000;">false</span>
<span class="lnr" style="color: #804000;"> 2 </span><span class="Identifier" style="color: teal;">CatchException.enabled</span>=<span class="Constant" style="color: #c00000;">false</span>
<span class="lnr" style="color: #804000;"> 3 </span><span class="Identifier" style="color: teal;">CatchThrowable.enabled</span>=<span class="Constant" style="color: #c00000;">false</span>
<span class="lnr" style="color: #804000;"> 4 </span><span class="Identifier" style="color: teal;">ThrowException.enabled</span>=<span class="Constant" style="color: #c00000;">false</span>
<span class="lnr" style="color: #804000;"> 5 </span><span class="Identifier" style="color: teal;">ThrowRuntimeException.enabled</span>=<span class="Constant" style="color: #c00000;">false</span>
<span class="lnr" style="color: #804000;"> 6 </span><span class="Identifier" style="color: teal;">GrailsStatelessService.enabled</span>=<span class="Constant" style="color: #c00000;">false</span>
<span class="lnr" style="color: #804000;"> 7 </span><span class="Identifier" style="color: teal;">NestedBlockDepth.maxNestedBlockDepth</span>=<span class="Constant" style="color: #c00000;">3</span>
<span class="lnr" style="color: #804000;"> 8 </span><span class="Identifier" style="color: teal;">CrapMetric.enabled</span>=<span class="Constant" style="color: #c00000;">true</span>
<span class="lnr" style="color: #804000;"> 9 </span><span class="Identifier" style="color: teal;">CrapMetric.coberturaXmlFile</span>=<span class="Constant" style="color: #c00000;">file:target/test-reports/cobertura/coverage.xml</span>
</pre>
<br />
Ahora solo basta ejecutar:<br />
grails test-app -unit -coverage -xml<br />
grails codenarc<br />
<br />
Como resultado tendremos más violaciones a las reglas pero seguro ayuda a mejorar en algo.<br />
<h3 class="fileHeader" style="font-family: Arial, sans-serif; font-size: 19px; margin-left: 10px; margin-top: 30px;">
➥ ItemGroupsController.groovy</h3>
<table border="1" style="-webkit-box-shadow: rgb(170, 170, 170) 3px 3px 4px; border-collapse: collapse; border-style: solid; border-width: 2px; box-shadow: rgb(170, 170, 170) 3px 3px 4px; font-family: Arial, sans-serif;"><tbody>
<tr class="tableHeader" style="font-weight: bold;"><th style="background-color: #ddddff; border-color: rgb(211, 211, 211) rgb(211, 211, 211) gray; border-style: solid; border-width: 1px; padding: 4px 20px; text-shadow: white 2px 2px 2px;">Rule Name</th><th style="background-color: #ddddff; border-color: rgb(211, 211, 211) rgb(211, 211, 211) gray; border-style: solid; border-width: 1px; padding: 4px 20px; text-shadow: white 2px 2px 2px;">Priority</th><th style="background-color: #ddddff; border-color: rgb(211, 211, 211) rgb(211, 211, 211) gray; border-style: solid; border-width: 1px; padding: 4px 20px; text-shadow: white 2px 2px 2px;">Line #</th><th style="background-color: #ddddff; border-color: rgb(211, 211, 211) rgb(211, 211, 211) gray; border-style: solid; border-width: 1px; padding: 4px 20px; text-shadow: white 2px 2px 2px;">Source Line / Message</th></tr>
<tr><td style="border: 1px solid rgb(211, 211, 211); padding: 4px 20px;"><a href="https://www.blogger.com/blogger.g?blogID=7890269#CrapMetric" style="color: #d93544;">CrapMetric</a></td><td class="priority2" style="background-color: #ffccaa; border: 1px solid rgb(211, 211, 211); color: #990000; font-weight: bold; padding: 4px 20px; text-align: center;">2</td><td class="number" style="border: 1px solid rgb(211, 211, 211); padding: 4px 20px; text-align: center;">76</td><td style="border: 1px solid rgb(211, 211, 211); padding: 4px 20px;"><div class="violationInfo" style="margin-bottom: 2px; margin-top: 2px;">
<span class="violationInfoPrefix" style="color: darkgrey; font-size: 10px; padding-right: 4px; width: 30px;">[SRC]</span><span class="sourceCode" style="color: #444444; font-size: 13px;">def update() {</span></div>
<div class="violationInfo" style="margin-bottom: 2px; margin-top: 2px;">
<span class="violationInfoPrefix" style="color: darkgrey; font-size: 10px; padding-right: 4px; width: 30px;">[MSG]</span><span class="violationMessage" style="font-size: 13px; font-style: italic;">Violation in class com.winbits.api.catalog.controllers.ItemGroupsController. The CRAP score for method [update] is [90.0]</span></div>
</td></tr>
</tbody></table>
<br />
<br />
El límite está puesto a 30 como máximo, este método tiene un índice de 90 lo cual nos indica que esta muy crapy, para poder reducir el índice se puede hacer un refactor para reducir su complejidad o aumentar sus pruebas automatizadas, claro que si se hace un refactor primero se tienen que hacer las pruebas para que sea seguro hacerlo :-)<br />
<br />
<div>
<br /></div>
<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-34807141745483424322013-01-20T14:44:00.000-06:002013-01-24T23:11:47.136-06:00Capcom SNES jostickHace rato que <a href="https://github.com/hgmiguel/arduino-stuff/tree/master/vim_pedal">tengo un botón hecho con arduino</a>, este <a href="http://hackaday.com/2012/06/21/building-a-clutch-for-vim/">botón</a> hace que <a href="http://www.vim.org/">vim</a> cambie de modo comando a modo inserción. Un amigo vio como lo ocupaba y se le ocurrió la idea de transformar un control capcom power stick para el <a href="http://en.wikipedia.org/wiki/Super_Nintendo_Entertainment_System">snes</a> y pasarlo a usb para poder utilizarlo como un controlador para <a href="http://www.native-instruments.com/#/en/products/dj/traktor/">Traktor</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSACYsK6pL5KFefd1PPz2YvwlzQpLL9ghB77H9hoyiCO7kWel4AEr8al9xty7LXmHQHA2A31TPtIDN-_hbH97sVwSBAnpLAx05o21nqDP6mx7UzT7m5Y6Lklb58xKkXIeDmVwCnA/s1600/2012-09-13+20.49.57.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSACYsK6pL5KFefd1PPz2YvwlzQpLL9ghB77H9hoyiCO7kWel4AEr8al9xty7LXmHQHA2A31TPtIDN-_hbH97sVwSBAnpLAx05o21nqDP6mx7UzT7m5Y6Lklb58xKkXIeDmVwCnA/s400/2012-09-13+20.49.57.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
He aquí como se transformó:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeCyMtpa3HK58SzbSxZ1iWgFeuPp2qeliTtiuabkz6dXDr4dd8RVCNAa1jmhBC1XxicOV1r_oSH4OWL7GnJCTmRpmjdcIq1o2p9uTI8DQBevmMWw0nWtx-gspGhi5iiE7jGGc1Sw/s1600/2012-09-23+14.56.26.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeCyMtpa3HK58SzbSxZ1iWgFeuPp2qeliTtiuabkz6dXDr4dd8RVCNAa1jmhBC1XxicOV1r_oSH4OWL7GnJCTmRpmjdcIq1o2p9uTI8DQBevmMWw0nWtx-gspGhi5iiE7jGGc1Sw/s400/2012-09-23+14.56.26.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Después de un tiempo de observación noté que una vez que le pasas tierra o corriente a la placa toda esta queda con electricidad, lo que nos facilita la tarea de cablear y ver como funciona, el integrado que hace que funcione con el snes no lo ocupamos para nada, y creo que sería mejor desoldarlo en algún momento. Para estar seguros ayuda mucho tener un multimetro.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYMmhZf11hV3zrBbMtowtSseZSRBbJu0G0cqPeY2oamPMojC5nm4UUsvyIwWS5rkI4JfhsBYmI00QR6OvqEFzm1-GqjyIMrhLgh1If9ut18h45Ip5carQvN1ADZCLiPrDJzQsHeQ/s1600/commonground.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYMmhZf11hV3zrBbMtowtSseZSRBbJu0G0cqPeY2oamPMojC5nm4UUsvyIwWS5rkI4JfhsBYmI00QR6OvqEFzm1-GqjyIMrhLgh1If9ut18h45Ip5carQvN1ADZCLiPrDJzQsHeQ/s400/commonground.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Así que fue cosa de soldar un pequeño dispositivo para poder conectarla a la corriente del <a href="http://arduino.cc/">arduino</a> y empezar a jugar un poco con las conexiones, después fue ir soldando las terminales de los botones y conectarlas directo a una terminal del arduino.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_1JqUOpVAGf7NFaKCELRuK5MPj1eQum9l65yfvyIgCTJdlkPpLE29fh8V6iufMlmWfdyJr8xIXajCY5HSS_E9f40C8NY_3m4OJ8LRc4-FEbRX8Cly6iEvm2ALl5Cq3pIM8Etm2w/s1600/2012-11-12+21.18.20.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_1JqUOpVAGf7NFaKCELRuK5MPj1eQum9l65yfvyIgCTJdlkPpLE29fh8V6iufMlmWfdyJr8xIXajCY5HSS_E9f40C8NY_3m4OJ8LRc4-FEbRX8Cly6iEvm2ALl5Cq3pIM8Etm2w/s200/2012-11-12+21.18.20.jpg" width="200" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5tWLx66qRQzBT6rMSNB2oNSM0r1qH9Yskpzz2ORwSBkZqtlD4vDnDrg2_7kaMmFZkFn4aJ352I2buaE6PEX7dDX2GjrBkRfeyoWKMzMEe4eMVkG_3lrOd39-Q5y5hKIwvZDdy7A/s1600/2012-11-12+22.43.58.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5tWLx66qRQzBT6rMSNB2oNSM0r1qH9Yskpzz2ORwSBkZqtlD4vDnDrg2_7kaMmFZkFn4aJ352I2buaE6PEX7dDX2GjrBkRfeyoWKMzMEe4eMVkG_3lrOd39-Q5y5hKIwvZDdy7A/s320/2012-11-12+22.43.58.jpg" width="320" /></a><br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
El primer intento lo hice con el <a href="https://www.sparkfun.com/products/10536">arduino pro ethernet de sparkfun</a>, pero para que funcionará necesitaría de otro programa que emulará los tecladazos debido a que este arduino no es reconocido como una <a href="http://en.wikipedia.org/wiki/USB_human_interface_device_class">interfaz usb hid</a>, así que decidí comprar un <a href="http://arduino.cc/en/Main/arduinoBoardLeonardo">arduino leonardo</a> para este caso.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCJ4npdW_E8LIqp8lTRIfmdgYp4jnWIOe2zOc-NWb_xODeNByMAmSRkjUPtQici3lgkjfE6UkP5NHUf-vmtvjBSbQ1L190Ssxg_SiLO7z4muUDT2ChkJ5Hpp6b21yAlFsqlQh34g/s1600/2013-01-06+14.16.32.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCJ4npdW_E8LIqp8lTRIfmdgYp4jnWIOe2zOc-NWb_xODeNByMAmSRkjUPtQici3lgkjfE6UkP5NHUf-vmtvjBSbQ1L190Ssxg_SiLO7z4muUDT2ChkJ5Hpp6b21yAlFsqlQh34g/s640/2013-01-06+14.16.32.jpg" width="640" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
El código puede obtenerse de <a href="https://github.com/hgmiguel/arduino-stuff">aqui</a>, esta es la parte que lee los botones, utilicé la variable <i>arrayLettersBits</i> para pasarle la tecla que quería que se presionara:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<pre class="brush: cpp"> //Letras a pulsar en el teclado
const String arrayLettersBits = "arbxyl512346";
for (int i = 2; i <= maxButtonPin; i++) {
buttonState = digitalRead(i);
if (buttonState != HIGH) {
Keyboard.press(arrayLettersBits[i-2]);
} else {
Keyboard.release(arrayLettersBits[i-2]);
}
}
</pre>
<br />
Esta parte es la que lee la palanca, aqui utilicé un pequeño hack, puesto que los switches del Jostick me regresaban aveces 0.1v o algo así y el arduino lo estaba detectando como <a href="http://arduino.cc/en/Reference/Constants">HIGH</a>, es por esto que utilicé <a href="http://arduino.cc/en/Reference/analogRead">analogRead</a> en vez de <a href="http://arduino.cc/en/Reference/digitalRead">digitalRead</a>.<br />
<br />
<pre class="brush: cpp">for (int i = jostikInitPin; i <= jostikEndPin; i++) {
buttonState = analogRead(i);
if (buttonState == 1023.00) {
switch(i) {
case 0:
delay(5);
buttonState = analogRead(i);
if (buttonState == 1023.00) {
Keyboard.press(KEY_RIGHT_ARROW);
jostickBits[i-jostikInitPin]='0';
}
break;
case 1:
// Más código similar aqui
}
} else {
switch(i) {
case 0:
Keyboard.release(KEY_RIGHT_ARROW);
break;
case 1:
// Más código similar aqui
}
}
</pre>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Al final lo terminé probando con un emulador de SNES y un juego de street fighter.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8TCw0wVq6y4573032nCPxFfYGWzVnBn1LBrlyHWvioqnyAAShy5Kw_GEw0__Yky6Yym9fK6y9NvUnPVp1HU38aWXvKW0nKiAeb68B5x8beTsUedgORkl9aoxPNhSOffWhR1RAUQ/s1600/2013-01-12+11.16.58.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8TCw0wVq6y4573032nCPxFfYGWzVnBn1LBrlyHWvioqnyAAShy5Kw_GEw0__Yky6Yym9fK6y9NvUnPVp1HU38aWXvKW0nKiAeb68B5x8beTsUedgORkl9aoxPNhSOffWhR1RAUQ/s400/2013-01-12+11.16.58.jpg" width="300" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Pero el verdadero uso será así:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbJ7w3O8F3oYMj_aHGQ5k8TpVViPGRl4z0bjz3j9qefgyFqu5GQeIhZkQGX0XKHB-GgIbKiMswnxDHHgFmJHDX_MEQM8wvL68FcN5Nir3CDDQptcOGAhhBnzVY2MKgmO5_GtQd2Q/s1600/traktor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbJ7w3O8F3oYMj_aHGQ5k8TpVViPGRl4z0bjz3j9qefgyFqu5GQeIhZkQGX0XKHB-GgIbKiMswnxDHHgFmJHDX_MEQM8wvL68FcN5Nir3CDDQptcOGAhhBnzVY2MKgmO5_GtQd2Q/s640/traktor.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-30684663719538903112012-10-22T23:29:00.000-05:002012-10-22T23:29:11.363-05:00namedqueries con left join<div>
Una de las funciones que más me gustan de <a href="http://grails.org/doc/latest/guide/GORM.html">gorm</a> es la de <a href="http://grails.org/doc/latest/ref/Domain%20Classes/namedQueries.html">namedQueries</a>, que no son otra cosa que queries que viven dentro de las clases de dominio a los cuales se les puede aplicar metodos como list, count, y pasarles argumentos, etc.</div>
<div>
<br /></div>
<div>
Supongamos que tenemos la siguiente estructura de BD</div>
<div>
<br /></div>
<div>
Persona -> Ocupacion -> Categoria</div>
<div>
<br /></div>
<div>
Y que queremos contar el numero de personas por categoría, incluyendo aquellas que no tengan ocupacion (ocupacion = null), el problema es que para llegar a la solución necesitamos de dos left join con sql, recordando que los nameqQueries utilizan la misma nomenclatura que los criteria builder se nos ocurre que hay que ocupar CriteriaSpecification.LEFT_JOIN.</div>
<div>
<br /></div>
<div>
El siguiente namedQuerie dentro de persona hará nos solucionará el problema</div>
<div>
<br /></div>
<div>
<div>
groupByCategoria {params -></div>
<div>
ocupacion(CriteriaSpecification.LEFT_JOIN) {</div>
<div>
categoria(CriteriaSpecification.LEFT_JOIN) { }</div>
<div>
projections {</div>
<div>
groupProperty("categoria")</div>
<div>
rowCount("total")</div>
<div>
}</div>
<div>
}</div>
<div>
}</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Y ya podremos ocuparlo de la siguiente manera:</div>
<div>
<br /></div>
<div>
Persona.groupByCategoria.list()</div>
<div>
<br /></div>
<div>
Esto generará una lista como la siguiente:</div>
<div>
[Categoria : 1:3, Categoria : 2:1, null:1]</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Creo que nunca me va a gustar hibernate, sobre todo porque le quita todo el poder del lenguaje sql, peor bueno, cuando no hay otra solución más vale aprender bien la tecnología usada.</div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-83131802851726316712012-08-19T23:05:00.000-05:002012-08-19T23:05:41.391-05:00Sobre grails y plugins<br />
<span style="font-family: Arial, Helvetica, sans-serif;">Por curiosas razones (ocupar <a href="http://www.cherokee-project.com/">cherokee</a> como proxy server y no tener un módulo de <a href="http://code.google.com/p/cherokee/issues/detail?id=388&q=ajp">AJP</a>) tuvimos un bug con recursos protegidos por SSL, en concreto tomcat nos regresaba recursos con http en vez de https...</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">La primera aproximación fue excluir los recursos que daban problemas, lo cual no iba a prevenir que esto siguiera pasando, en la <a href="http://grails-plugins.github.com/grails-resources/guide/9.%20Configuration.html">configuración</a> se detalla como hacerlo, en concreto hay que usar </span><strong class="bold" style="background-color: white; color: #444444; font-family: Arial; font-size: 14px; line-height: 16px; text-align: justify;">grails.resources.adhoc.excludes </strong><span class="bold" style="background-color: white; color: #444444; line-height: 16px; text-align: justify;"><span style="font-family: Arial, Helvetica, sans-serif;">para excluir los recursos que causan problemas.</span></span><br />
<span class="bold" style="background-color: white; color: #444444; font-family: Arial; font-size: 14px; line-height: 16px; text-align: justify;"><br /></span>
<span class="bold" style="background-color: white; color: #444444; font-family: Arial; font-size: 14px; line-height: 16px; text-align: justify;">Cuando volvió a producirse el error se investigó mejor y se analizó el código del plugin de <a href="http://grails-plugins.github.com/grails-resources/guide/index.html">resurces</a>, de lo cual se llego a un @todo que nos daba problemas... </span><br />
<span class="bold" style="background-color: white; color: #444444; font-family: Arial; font-size: 14px; line-height: 16px; text-align: justify;"><br /></span>
<span class="bold" style="background-color: white; color: #444444; font-family: Arial; text-align: justify;"></span><br />
<span class="bold" style="background-color: white; font-family: Courier New, Courier, monospace; font-size: x-small; text-align: justify;"><span style="line-height: 16px;"><span style="color: #444444;">○ → </span><b><span style="color: #444444;">find . -name "*.groovy" | xargs grep -i </span><span style="color: red;">todo</span><span style="color: #444444;"> | awk -F'/' '{out=$8; for(i=9;i<=NF;i++){out=out" "$i};print out}'</span></b></span></span><br />
<br />
<div style="text-align: justify;">
<span style="color: #444444; font-family: Courier New, Courier, monospace; font-size: x-small;"><span style="line-height: 16px;">ResourceTagLib.groovy: @todo where a module resource is bundled, need to satisfy deps of all resources in the bundle first!</span></span></div>
<div style="text-align: justify;">
<span style="color: #444444; font-family: Courier New, Courier, monospace; font-size: x-small;"><span style="line-height: 16px;">ResourceTagLib.groovy: * @todo Later, we implement ESP hooks here and add scope="user" or scope="shared"</span></span></div>
<div style="text-align: justify;">
<span style="color: #444444; font-family: Courier New, Courier, monospace; font-size: x-small;"><span style="line-height: 16px;">ResourceTagLib.groovy: def baseUrl = '' @todo get from config</span></span></div>
<div style="text-align: justify;">
<span style="color: #444444; font-family: Courier New, Courier, monospace; font-size: x-small;"><span style="background-color: #f1c232; line-height: 16px;"><b>ResourceTagLib.groovy: @todo do we need to toggle http https here based on current request protocol?</b></span></span></div>
<div style="text-align: justify;">
<span style="color: #444444; font-family: Courier New, Courier, monospace; font-size: x-small;"><span style="line-height: 16px;">ResourceTagLib.groovy: * @todo this currently won't work for absolute="true" invocations, it should just passthrough these</span></span></div>
<div style="text-align: justify;">
<br /></div>
<div>
Navegando por las entrañables aguas del código fuente ajeno llegamos a la función que nos daba el error <a href="https://github.com/grails-plugins/grails-resources/blob/master/src/groovy/org/grails/plugin/resource/ResourceProcessor.groovy#L241">redirectToActualUrl</a>, la solución fácil y para no esperar una actualización en el plugin fue sustituir la función por medio de <a href="http://groovy.codehaus.org/Dynamic+Groovy">metaprogramación</a> en groovy.<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ResourceProcessor.<b>metaClass</b>.redirectToActualUrl = {ResourceMeta res, request, response -></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> //... Codigo ...</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> def u = (grailsApplication.config.grails.serverURL? grailsApplication.config.grails.serverURL.toString() : request.contextPath) + staticUrlPrefix+res.linkUrl</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> //... Codigo ...</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-size: x-small;"> }</span></span><br />
<div>
<br /></div>
<br />
Como moraleja no hay que confiar mucho en plugins ajenos, hay por todos lados @todo, uno de los que más me afectan son las pruebas unitarias, no toda la funcionalidad de GORM esta integrada, casi siempre se termina haciendo pruebas de integración para solventar este problema. Aún así <a href="http://grails.org/doc/latest/">grails</a> es una herramienta excelente para programar aplicaciones web, no regresaría a java + frameworks para esto...<br />
<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-42168679347420713892012-08-18T19:15:00.003-05:002012-08-18T19:15:45.846-05:00ssl y cherokeeNadie va a negar que <a href="http://www.godaddy.com/">godaddy</a> vende certificados muy baratos en comparación con <a href="http://www.verisign.com/">verisign</a>, así que para empresas de nueva creación godaddy es la opción... Y por barato los problemas no tardan en aparecer.<br />
<br />
Nuestro certificado marcó problemas con dispositivos móviles. En concreto no reconocía a la autoridad certificadora. Luego de investigar y de no tener idea en que parte había hecho las cosas mal descubrí en el siguiente blog la solución al problema... Y era tan simple como concatenar el archivo gd_bundle al certificado:<br />
<br />
$cat gd_bundle.crt >> dominio.com.crt<br />
<br />
powered by <a href="http://www.cherokee-project.com/">cherokee</a><br />
<br />
Dejo la nota original.<br />
<span style="color: #0000ee; text-decoration: underline;">http://fluidsurveys.com/cherokee-web-server-ssl-setup/</span><br />
<br />
<span style="background-color: white; font-family: open-sans-1, open-sans-2, sans-serif; font-size: 13px; line-height: 24px;">After trying many things, and contacting godaddy, who told me they did not support Cherokee, I finally managed to figure out how to get it to work. The answer is pretty simple, if you follow the rest of the directions about setting up SSL with Cherokee, pointing your virtual server to the correct ssl certificate (Ours was fluidsurveys.com.crt) and the proper key (fluidsurveys.com.key) there was no place to input the gd_bundle.crt (The inermediate certificate package that godaddy provided us). The solution is to combine your site certificate (fluidsurveys.com.key) with your bundle certificate, root certificate, or intermediate certificates. To do this, simply make a backup of your site certificate (fluidsurveys.com.crt), then cat the intermediate bundle with the original site certificate. “</span><em style="background-color: white; font-family: open-sans-1, open-sans-2, sans-serif; font-size: 13px; line-height: 24px; margin: 0px; padding: 0px;">cat gd_bundle.crt >> fluidsurveys.com.crt</em><span style="background-color: white; font-family: open-sans-1, open-sans-2, sans-serif; font-size: 13px; line-height: 24px;">” This should now solve your issue with IE6 not recognizing the issuer of your SSL certificate.</span><br />
<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-5402597440121422262012-04-11T23:32:00.002-05:002012-04-11T23:42:12.349-05:00Sobre los archivos .desktop<div><span >Tengo localizado mi linux/gnu/debian/Gnome en el idioma gabacho, pero hay una herramienta que hoy ocupo mucho y que es importante que este en mi idioma natal, español de México, por qué, si no entiendo algo podría cometer una equivocacion, este programa es <a href="http://homebank.free.fr/">homebank</a> </span></div><div><span ><br /></span></div><div><span >Bueno, la primera tarea fue hacer que la aplicación se ejecutara al principio, cuando se inicia sesión, no encontré nada fácil en gnome shell, así que googleando se llega a la solución:</span></div><div><span ><br /></span></div><div><span ><b>Meter un archivo .desktop en ~/.config/autostart/ </b></span></div><div><span ><br /></span></div><div><span >Para encontrar el archivo .desktop de homebank realicé la siguiente búsqueda:</span></div><div><span ></span></div><blockquote><div><span > chicuace1 in ~/</span></div><div><span >○ → find /usr/share/ -name "homebank*desktop"</span></div><div><span >/usr/share/app-install/desktop/homebank.desktop</span></div><div></div></blockquote><div><span ><br /></span></div><div><span >Una vez encontrado el archivo lo copiamos a la ruta de autostart y listo, volvemos ha entrar a la sesión y se ejecuta automáticamente. Hasta ahí todo bien, pero se sigue ejecutando con el locale de US.</span></div><div><span ><br /></span></div><div><span >Para cambiar el locale necesitamos saber si tenemos el locale que queremos, </span><span style="font-family: Georgia, serif; ">para eso ejecutamos:</span></div><div><span ><br /></span></div><div><span ></span></div><blockquote><div><span > chicuace1 in ~/</span></div><div><span >○ → <b>locale -a</b></span></div><div><span >C</span></div><div><span >C.UTF-8</span></div><div><span >en_US.utf8</span></div><div><span ><b><i>es_MX.utf8</i></b></span></div><div><span >POSIX</span></div></blockquote><div><span ></span></div><div><span ><br /></span></div><div><span ><br /></span></div><div><span >Y ahí esta, el que me interesa es <b><i>es_MX.utf8</i></b>, en caso de no hayarlo en mi debian debo de hacer dpkg-reconfigure locales, para otras distribuciones habrá </span><span style="font-family: Georgia, serif; ">un comando similar. Ahora si ejecutamos lo siguiente tendremos nuestra aplicación traducida al español</span></div><div><span ><br /></span></div><div><span ></span></div><blockquote><div><span > chicuace1 in ~/bin</span></div><div><span >○ → <b>LANG=es_MX.utf8 homebank</b></span></div></blockquote><div><span ></span></div><div><span ><br /></span></div><div><span ><br /></span></div><div><span >Bien, no encontré en la especificación de <a href="http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html">freedesktop</a> </span><span style="font-family: Georgia, serif; ">una manera de modificar el <i>LANG</i>, así que no me quedó de otra que crear un sh </span><span style="font-family: Georgia, serif; ">con esa instrucción :</span></div><div><span ><br /></span></div><div><span ></span></div><blockquote><div><span > chicuace1 in ~/bin</span></div><div><span >○ →<b> echo "LANG=es_MX.utf8 homebank" > homebank.sh && chmod a+x homebank.sh</b></span></div></blockquote><div><span ></span></div><div><span ><br /></span></div><div><span >Una vez probado ese script hay que modificar nuestro </span><span >~/.config/autostart/homebank.desktop sustituyendo Exec=homebank %F por </span><b style="font-family: Georgia, serif; ">Exec=/home/usuario/bin/homebank.sh %F</b></div><div><span ><br /></span></div><div><span >Y ahora ya tendremos nuestra aplicacion localizada al español y ejecutandoce </span><span style="font-family: Georgia, serif; ">cada inicio de sesión.</span></div><div><span style="font-family: Georgia, serif; "><br /></span></div><div><span style="font-family: Georgia, serif; ">P.D. si ven este símbolo </span><span style="font-family: Georgia, serif; ">○ →</span><span style="font-family: Georgia, serif; "> en mi shell es porque ocupo bash-it, les recomiendo ampliamente ocuparlo, muchos otros recomiendan <a href="http://zsh.sourceforge.net/">zsh</a>... </span></div>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-80884703577156177192011-11-10T23:56:00.013-06:002011-11-15T23:35:52.120-06:00Arduino Ethernet pro con linux<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinp4uStSDG7ZLm0G20ffBl1x2BXxOBsLIekOeyZbb2RMe9PW4ccy9mtpI0mVZhNr7f8NX9-pFsV6uDizqpD5cEujeyYiZ8fTtSnAu8Yt9jRD4GxL3ZCroTGVTQHXEN4HJ-NbRnrg/s1600/IMG00170-20111114-1655.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinp4uStSDG7ZLm0G20ffBl1x2BXxOBsLIekOeyZbb2RMe9PW4ccy9mtpI0mVZhNr7f8NX9-pFsV6uDizqpD5cEujeyYiZ8fTtSnAu8Yt9jRD4GxL3ZCroTGVTQHXEN4HJ-NbRnrg/s320/IMG00170-20111114-1655.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5675407895606430642" /></a>Luego de hacerle esa espantosa soldadura a mi placa arduino, me puse a hacer unos test para probar la funcionalidad del <a href="http://arduino.cc/">arduino</a> <a href="http://www.sparkfun.com/products/10536">ethernet pro</a>. De hecho esos headers quedaron chuecos...<div><br /></div><div>Aqui el primer problema es que uso Linux, y la <a href="http://www.arduino.cc/en/Reference/Ethernet">libreria Ethernet</a> del arduino no nos sirve (empieza a mandar basura). Buscando se recomienda cambiar esa librería por la <a href="http://code.google.com/p/tinkerit/source/browse/#svn/trunk/Ethernet2%20library/Ethernet2">Ethernet2</a>.</div><div><br /></div><div>Si solo la instalamos, en mi caso copiando los archivos a /usr/share/arduino/libraries/, nos van a salir errores de compilación, para resolverlos editamos el archivo Server.cpp y sustituimos #include "Ethernet.h" por #include "Ethernet2.h", el siguiente paso es borrar los archivos Print.cpp y Print.h</div><div><br /></div><div>Para probarlo simplemente hay que sustituir en nuestros proyectos Ethernet.h por Ethernet2.h</div><div><div><div><pre class="brush: cpp"></pre></div><br /><pre class="brush: cpp">#include <ethernet2.h><br /><br />byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };<br />byte ip[] = { 10, 198, 0, 2 };<br />byte server[] = { 10, 198, 0, 1 }; //My machine host<br /><br />Client client(server, 8080);<br /><br />const int buttonPin = 7; // the number of the pushbutton pin<br />const int ledPin = 3; // the number of the LED pin<br /><br />// Variables will change:<br />int lastButtonState = LOW; // the previous reading from the input pin<br /><br />void setup() {<br />Ethernet.begin(mac, ip);<br />Serial.begin(9600);<br /><br />//Esperamos a que se inicie la Ethernet<br />delay(1000);<br /><br />Serial.println("connecting...");<br /><br /><br />pinMode(buttonPin, INPUT);<br />pinMode(ledPin, OUTPUT);<br />}<br /><br />void reacciona(){<br />if (client.connect()) {<br /> Serial.println("connected");<br /> client.println("GET /Robotina/escucha/escucha HTTP/1.0");<br /> client.println();<br /><br /> if (client.available()) {<br /> for (int i; i<100; i=0) {<br /> char c = client.read();<br /> Serial.print(c);<br /> }<br /> }<br /> if (!client.connected()) {<br /> Serial.println();<br /> Serial.println("disconnecting.");<br /> client.stop();<br /> }<br /> } else {<br /> Serial.println("connection failed");<br /> } }<br /> void loop() {<br /> int reading = digitalRead(buttonPin);<br /> if (reading == HIGH && lastButtonState != reading) {<br /> reacciona();<br /> }<br /> if (lastButtonState!= reading) {<br /> lastButtonState = reading;<br /> }<br /> digitalWrite(ledPin, lastButtonState);<br />} </ethernet2.h></pre><br /><br /><div>Y este seria mi esquema:</div><div><br /></div><div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDiYsxBwc9RmghZ_a61LuLEn_aLFH62ceqlX0A6exm1TWVTAun-eoh7bx5wqMayivPSvfmBkJlUR1FKqKP5G_sf5kjzFI9jvNKwydw-99qgGD0PnoGTFiEUk5vaZcn43lecCbAaA/s1600/digital-input-analog-output-window-alarm_Window_Alarm_bb.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDiYsxBwc9RmghZ_a61LuLEn_aLFH62ceqlX0A6exm1TWVTAun-eoh7bx5wqMayivPSvfmBkJlUR1FKqKP5G_sf5kjzFI9jvNKwydw-99qgGD0PnoGTFiEUk5vaZcn43lecCbAaA/s320/digital-input-analog-output-window-alarm_Window_Alarm_bb.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5675427073900884258" style="cursor: pointer; width: 320px; height: 240px; " /></a></div><div><br /></div></div></div>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-56364071439469097942011-07-05T22:48:00.003-05:002011-07-05T23:24:35.893-05:00cd suffleHoy me hicieron el día: tenía tiempo que no encontraba una opción tan oscura y a la vez tan útil de un comando que uso diario, a todas horas y del cual pensaba ya no sacar más provecho, el hecho en cuestion es <a href="https://www.ibm.com/developerworks/aix/library/au-directorytree/index.html#cdshuffle">cd shuffle</a>...<br /><br />Cabe aclarar que sólo funciona con ksh, si estas con bash no funcionará... Pasemos al pedazo del man ksh que nos interesa:<br /><span><br /><span class="Apple-style-span" >cd [ -LP ] [ arg ]<br />cd [ -LP ] old new<br /> ..explicaciones mas, explicaciones menos...<br /> <b>The second form of cd substitutes the string new for the string old in the current directory name, PWD, and tries to change to this new directory.</b><br /> ..explicaciones mas, explicaciones menos...</span><br /></span><br />Y a la demostracion:<div><br /><span class="Apple-style-span" ><b>[root@macuile .gnome2]# ksh<br /># echo $SHELL</b><br />/bin/bash<br /><span class="Apple-style-span" >#Nota: no tengo idea de donde guarda ksh el shell utilizado actualmente</span></span></div><div><span class="Apple-style-span" ><span class="Apple-style-span" ># como podrán darse cuenta no se borró el SHELL anterior</span><br /><b># echo $$</b><br />12330<br /><b># ps -ef | grep $$</b><br />root 12330 12277 0 22:53 pts/0 00:00:00 ksh<br />root 12339 12330 0 22:53 pts/0 00:00:00 ps -ef<br />root 12340 12330 0 22:53 pts/0 00:00:00 grep 12330</span></div><div><span class="Apple-style-span" ><span class="Apple-style-span" >#Nota: con esto nos aseguramos de que sea ksh</span><br /><b># cd /home/db2inst1/.gnome2<br /># cd db2inst1 miguel</b><br />/home/miguel/.gnome2<br /><b># pwd</b><br />/home/miguel/.gnome2 </span></div><div><span class="Apple-style-span" ><span class="Apple-style-span" >#Nota: magia, nos cambiamos de directorio</span><br /><b>[root@macuile .gnome2]# echo $SHELL</b><br />/bin/bash<br /><b>[root@macuile .gnome2]# pwd</b><br />/home/db2inst1/.gnome2<br /><b>[root@macuile .gnome2]# cd db2inst1 miguel</b><br />-bash: cd: db2inst1: No such file or directory</span></div><div><span class="Apple-style-span" ><b>[root@macuile .gnome2]# mkdir db2inst1 miguel</b></span></div><div><span class="Apple-style-span" ><b>[root@macuile .gnome2]# cd db2inst1 miguel</b></span></div><div><span class="Apple-style-span" ><span class="Apple-style-span" >#Nota: A mi parecer este comportamiento es peor que no hacer nada y fallar</span><br /><b>[root@macuile db2inst1]# pwd</b><br />/home/db2inst1/.gnome2/db2inst1</span></div><div><span class="Apple-style-span" ><div><b>[root@macuile db2inst1]# mkdir miguel</b></div><div><b>[root@macuile db2inst1]# cd ..</b></div><div><b>[root@macuile .gnome2]# cd db2inst1 miguel</b></div><div><span class="Apple-style-span" >#Nota: sería interesante que hubiera entrado hasta miguel, no hubo suerte</span></div><div><b>[root@macuile db2inst1]# pwd</b></div><div>/home/db2inst1/.gnome2/db2inst1</div><div><b>[root@macuile db2inst1]# ls -l</b></div><div>total 4</div><div>drwxr-xr-x 2 root root 4096 Jul 5 23:20 miguel</div><div><br /></div></span></div><div><span class="Apple-style-span" ><br /></span></div>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-33937976157378559202011-02-02T21:02:00.004-06:002011-03-28T22:48:26.974-06:00Sobre la decompilación o la reingeniería<pre><br />De estos requerimientos que nadie quiere hacer y una vez hecho ya no se ocupan, la cosa va mas o menos asi:<br /><br />Hay varios appserver, cada uno con sus app, cada app contiene un jar (modelo.jar) que se ocupa, entre otras cosas, para saber<br />si la app se encuentra en produccion, desarrollo o pruebas. Muy listo el truco, la app se da cuenta mediante el hostname de la<br />maquina donde esta alojada la app. Erroneamente se pensaría que el modelo.jar es el mismo para todas las app, pero no, hay 260<br />y tantos jars diferentes y de los cuales yo no tenía ni idea de como funcionaban.<br /><br />solucion:<br /> Agarra todos los jar decompilar la clase que hace referencia al hostna, compilarla con el nuevo hostname y actualizar el jar...<br /> Y para esto nuestro buen amigo sed viene al rescate.<br /><br /><span class="lnr"> 1 </span><span class="Comment">#!/bin/ksh -</span><br /><span class="lnr"> 2 </span><span class="Comment">#===============================================================================</span><br /><span class="lnr"> 3 </span><span class="Comment">#</span><br /><span class="lnr"> 4 </span><span class="Comment"># FILE: reemplazar_ambiente.sh</span><br /><span class="lnr"> 5 </span><span class="Comment">#</span><br /><span class="lnr"> 6 </span><span class="Comment"># USAGE: ./reemplazar_ambiente.sh</span><br /><span class="lnr"> 7 </span><span class="Comment">#</span><br /><span class="lnr"> 8 </span><span class="Comment"># DESCRIPTION:</span><br /><span class="lnr"> 9 </span><span class="Comment">#</span><br /><span class="lnr">10 </span><span class="Comment"># OPTIONS: ---</span><br /><span class="lnr">11 </span><span class="Comment"># REQUIREMENTS: ---</span><br /><span class="lnr">12 </span><span class="Comment"># BUGS: ---</span><br /><span class="lnr">13 </span><span class="Comment"># NOTES: ---</span><br /><span class="lnr">14 </span><span class="Comment"># AUTHOR: Miguel Angel Huerta Gonzalez </span><br /><span class="lnr">15 </span><span class="Comment"># COMPANY:</span><br /><span class="lnr">16 </span><span class="Comment"># CREATED: 26/01/2011 12:35:06 p.m. Hora estándar central (México)</span><br /><span class="lnr">17 </span><span class="Comment"># REVISION: ---</span><br /><span class="lnr">18 </span><span class="Comment">#===============================================================================</span><br /><span class="lnr">19 </span><br /><span class="lnr">20 </span><span class="Statement">set</span> <span class="Special">-o</span> nounset <span class="Comment"># Treat unset variables as an error</span><br /><span class="lnr">21 </span><br /><span class="lnr">22 </span><span class="Identifier">JDEC</span>=/home/user/jode<span class="Constant">-1</span>.<span class="Constant">1</span>.<span class="Constant">2</span>-pre1.jar<br /><span class="lnr">23 </span><br /><span class="lnr">24 </span><span class="Comment">#A_REEMPLAZAR="/app-2709/webapps/WEB-INF/lib/modelo.jar \</span><br /><span class="lnr">25 </span><span class="Comment">#/app-2709/webapps1/WEB-INF/lib/modelo.jar \</span><br /><span class="lnr">26 </span><span class="Comment">#/app-2709/webapps3/WEB-INF/lib/modelo.jar \</span><br /><span class="lnr">27 </span><span class="Comment">#/app-2709/webapps4/WEB-INF/lib/modelo.jar"</span><br /><span class="lnr">28 </span><br /><span class="lnr">29 </span><span class="Statement">for</span> MODELO <span class="Statement">in</span> <span class="PreProc">$A_REEMPLAZAR</span> <span class="Statement">;</span> <span class="Statement">do</span><br /><span class="lnr">30 </span> <span class="Statement">echo</span><span class="Constant"> </span><span class="Statement">"</span><span class="Constant">=======================</span><span class="PreProc">$WEBAPP</span><span class="Constant">=============================</span><span class="Statement">"</span><br /><span class="lnr">31 </span> <span class="Statement">ls</span> <span class="Statement">-l</span> <span class="PreProc">$MODELO</span><br /><span class="lnr">32 </span> <span class="Statement">cp</span> <span class="PreProc">$MODELO</span> .<br /><span class="lnr">33 </span> <span class="Identifier">WEBAPP</span>=<span class="PreProc">$(</span><span class="Special">echo </span><span class="PreProc">$MODELO</span><span class="Special"> </span><span class="Statement">|</span><span class="Special"> awk -F/ </span><span class="Statement">'</span><span class="Constant">{printf "%s/%s\n", $6,$7}</span><span class="Statement">'</span><span class="PreProc">)</span><br /><span class="lnr">34 </span> <span class="Statement">mkdir</span> <span class="Statement">-p</span> respaldo/<span class="PreProc">$WEBAPP</span><br /><span class="lnr">35 </span> <span class="Statement">cp</span> <span class="PreProc">$MODELO</span> respaldo/<span class="PreProc">$WEBAPP</span><br /><span class="lnr">36 </span> <span class="Identifier">AMBIENTE</span>=<span class="PreProc">$(</span><span class="Special">jar tf modelo.jar </span><span class="Statement">|</span><span class="Special"> </span><span class="Statement">grep</span><span class="Special"> Ambiente</span><span class="PreProc">)</span><br /><span class="lnr">37 </span> <span class="Statement">echo</span><span class="Constant"> </span><span class="PreProc">$AMBIENTE</span><br /><span class="lnr">38 </span> <span class="Statement">mkdir</span> <span class="Statement">-p</span> <span class="PreProc">$(</span><span class="Special">dirname </span><span class="PreProc">$AMBIENTE</span><span class="PreProc">)</span><br /><span class="lnr">39 </span> java -<span class="Statement">cp</span> <span class="PreProc">$JDEC</span> jode.decompiler.Main <span class="Statement">-c</span> modelo.jar com.app.modelo.apConfig.Ambiente <span class="Statement">></span> Ambiente.java<br /><span class="lnr">40 </span> <span class="Identifier">FILE</span>=<span class="PreProc">$(</span><span class="Special">dirname </span><span class="PreProc">$AMBIENTE</span><span class="PreProc">)</span><br /><span class="lnr">41 </span> <span class="Statement">sed</span> <span class="Statement">'</span><br /><span class="lnr">42 </span><span class="Constant"> /apphostname.*PRODUCCION/ i\</span><br /><span class="lnr">43 </span><span class="Constant"> ambientes.put("apphostnamenew".toUpperCase(), PRODUCCION);</span><br /><span class="lnr">44 </span><span class="Constant"> </span><span class="Statement">'</span> Ambiente.java <span class="Statement">></span> Ambiente2.java<br /><span class="lnr">45 </span> <span class="Statement">mv</span> Ambiente2.java Ambiente.java<br /><span class="lnr">46 </span> javac -classpath modelo.jar Ambiente.java<br /><span class="lnr">47 </span> <span class="Statement">mv</span> Ambiente.class <span class="PreProc">$(</span><span class="Special">dirname </span><span class="PreProc">$AMBIENTE</span><span class="PreProc">)</span>/<br /><span class="lnr">48 </span> jar uf modelo.jar <span class="PreProc">$AMBIENTE</span><br /><span class="lnr">49 </span> <span class="Statement">mkdir</span> <span class="Statement">-p</span> output/<span class="PreProc">$WEBAPP</span><br /><span class="lnr">50 </span> <span class="Comment">#cp modelo.jar output/$WEBAPP</span><br /><span class="lnr">51 </span> <span class="Statement">cp</span> modelo.jar <span class="PreProc">$MODELO</span><br /><span class="lnr">52 </span> <span class="Statement">echo</span><span class="Constant"> </span><span class="Statement">"</span><span class="Constant">=======================</span><span class="PreProc">$WEBAPP</span><span class="Constant">=============================</span><span class="Statement">"</span><br /><span class="lnr">53 </span><span class="Statement">done</span><br /><span class="lnr">54 </span><br /></pre>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-5351348996736126512011-01-16T18:01:00.003-06:002011-04-04T10:23:36.852-05:00Sobre el spam y las cadenaComo no todo lo que esta por internet es cierto, me comentaron de una cadena muy curiosa, la cosa iba más o menos asi:<br /><br />Bla bla bla, este mes es especial porque tiene 5 Lunes, 5 Sábados y 5 Domingos, y esto solo se repite cada 834 años... bla bla bla si no mandas esto tendras 5 años de perdición...<br /><br />Y como todos los fines de semana termino haciendo muchas cosas menos las que tenía que hacer, he aqui que esta cadena es una mentira...<br /><br /><pre><span><strong>#!/bin/bash - </strong></span><br /><br /><span>ANIO</span>=1176<br />while [ <span>$ANIO</span> -lt 2011 ]; do<br /><span>ANIO</span>=$((<span>$ANIO</span>+1))<br />if [ <span>"$(cal 1 <span>$ANIO</span>|tail -n+3)"</span> == <span>"$(cal|tail -n+3)"</span> ]; then<br /> echo <span>"otro año maravilloso <span>$ANIO</span>"</span><br />fi<br />done<br /></pre><br /><br /><span class="Apple-style-span"></span><span class="Apple-style-span">$ ./prueba_cadena.sh otro año maravilloso 1177<br />otro año maravilloso 1183<br />otro año maravilloso 1194<br />...<br /><br />otro año maravilloso 1994<br />otro año maravilloso 2000<br />otro año maravilloso 2005<br />otro año maravilloso 2011<br />$</span><br /><br />Pues parece que tenemos bastantes años más para pedir nuestros deseos...<br /><br /><br /><span class="Apple-style-span"><span class="Apple-style-span">$ cal 1 2005<br />Enero 2005<br />do lu ma mi ju vi sá<br /> 1<br />2 3 4 5 6 7 8<br />9 10 11 12 13 14 15<br />16 17 18 19 20 21 22<br />23 24 25 26 27 28 29<br />30 31<br />$ cal<br />Enero 2011<br />do lu ma mi ju vi sá<br /> 1<br />2 3 4 5 6 7 8<br />9 10 11 12 13 14 15<br />16 17 18 19 20 21 22<br />23 24 25 26 27 28 29<br />30 31<br />$<br /></span></span><br /><br />En fin, es una estupidez haberlo comprobado, pero me sirve para repasar el scripting.<br /><br />Y entre otros temas, <a href="http://opencv.willowgarage.com/wiki/">opencv</a>; cuando me siento frente a mi lap siento que alguien me observa, y asi es, el lente de la webcam que viene inscrutada por defecto (en estricto sentido el defecto), pero se le puede sacar algun provecho..<br /><br /><iframe width="480" height="295" src="http://www.youtube.com/embed/Fjj9gqTCTfc?fs=1" frameborder="0"></iframe><br /><br />Y la cosa funciona, he modificado un poco un ejemplo que viene por ahi <a href="http://code.google.com/p/ctypes-opencv/source/browse/trunk/demo/lkdemo.py?r=230">lkdemo.py</a> y la cosa anda, se puede mover el mouse con las manos, y ahi estoy, falta hacer algo como un lenguaje para interactuar con la pantalla...Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-52667660586145513962011-01-05T20:28:00.003-06:002011-01-05T23:16:07.515-06:00Todo es un archivoTodo es un archivo... Es una de las cosas que se aprende en UNIX/Linux, que todo es un archivo, pero hay diferentes tipos de archivos, revisemos <a href="http://unixhelp.ed.ac.uk/CGI/man-cgi?test">man test</a>:<div><br />-b FILE<br />FILE exists and is block special<br />-c FILE<br />FILE exists and is character special<br />-d FILE<br />FILE exists and is a directory<br />-f FILE<br />FILE exists and is a regular file<br />-h FILE<br />FILE exists and is a symbolic link (same as -L)<br />-S FILE<br />FILE exists and is a socket<br /><br />Claro, nada es tan simple como parece y hay muchos tipos de archivos :-), y con este preámbulo entremos en materia...<br />Si has usado algún comando y le has pasado la entrada estándar, por ejemplo cat < file.in, se te habrá ocurrido hacer lo mismo para tus scripts. Me llevo un tiempo encontrar una solución para este problema:<br /><pre name="code" class="js"><br />if [ ! -c /proc/$$/fd/0 ]<br />then<br />MENSAJE=$(cat -)<br />else<br />MENSAJE=""<br />fi<br /></pre><br /><br />Cada proceso crea dentro de /proc/PID/fd/ la entrada estándar 0, salida estándar 1, y error estándar 2, lo que hacemos es comprobar que la entrada estándar del shell no sea un fichero de carácter especial (en este caso el teclado). Podemos probar que tipo de archivo es con estos comandos:<br /><br /><pre name="code" class="js"><br />$ file /dev/fd/0 <br />/dev/fd/0: symbolic link to `/dev/pts/0'<br />$ file /dev/pts/0<br />/dev/pts/0: character special<br /></pre><br />Se me hizo más fácil comprobar solo que no sea de tipo "character special", pero bien pudiéramos solo aceptar archivos normales:<br /><pre name="code" class="js"><br />if [ -f /proc/$$/fd/0 ]<br /></pre><br />$(cat -) lee la entrada estándar, si no hiciéramos la comprobación se quedaría esperando a que tecleáramos ctrl + d<br /><br />Ya con esto podemos invocar nuestro script de estas dos formas sin problemas:<br /><br />script.sh < archvio.txt<br />script.sh<br /><br />a mi me sirvió para esto:<br /><pre name="code" class="js"><br />cat <<EOF | (cat -; for i in $@; do uuencode $i $(basename $i);done ) | mailx -s "$SUBJECT" $CORREO<br />$MENSAJE<br />EOF<br /></pre>Cosa que requiere más explicación y muchos más conceptos que no creo que llegue a explicar :-)...<br /><br />nota: $$ nos devuelve el pid del programa en ejecución. Si son curiosos hagan un ls -l /proc/$$<br /><br /><br /></div>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-74788614625028053112010-09-05T21:13:00.002-05:002011-07-09T17:37:15.374-05:00SQL antipatterns<p>Termine de leer otro libro, <a href="http://www.pragprog.com/titles/bksqla/sql-antipatterns">SQL Antipatterns</a>, veamos. Ultimamente han proliferado los ORM, como hibernate, y con el surgimiento de ruby on rails, el patrón de<a href="http://en.wikipedia.org/wiki/ActiveRecord"> ActiveRecord</a>; ¿qué es lo que esto ha ocacionado?, pues un completo desperdicio de los motores de base de datos, y un aumento de antipatrones en el diseño de la DB. Sin mas con este libro te sorprenderás de lo fácil que es hacer queries y hacerlos bien, nunca he entendido como es que se prefiere la complejidad de hsql, el no entender que SQL es un <a href="http://es.wikipedia.org/wiki/Programaci%C3%B3n_declarativa">lenguaje declarativo</a> es de los peores errores de un programador, a SQL le dices que es lo que quieres, no las instrucciones de como hacer lo que quieres.</p><p>Es un libro ligero, de una buena lectura, que si tienes algo de experiencia en diferentes proyectos, te darás cuenta que tan recuerrente son estas malas prácticas y cuando leas la solución verás que era muy fácil no caer en ellas.</p><p>Aqui algunas anotaciones de mi kindle:</p><p></p><p><span class="Apple-style-span">==========</span></p><p><span class="Apple-style-span">SQL Antipatterns</span></p><p><span class="Apple-style-span">- Highlight Loc. 1699-1704 | Added on Sunday, August 08, 2010, 04:35 PM</span></p><p><span class="Apple-style-span" style="font-family: 'courier new'; font-size: x-small; ">SELECT * FROM Comments AS c LEFT OUTER JOIN (BugsComments JOIN Bugs AS b USING (issue_id)) USING (comment_id) LEFT OUTER JOIN (FeaturesComments JOIN FeatureRequests AS f USING (issue_id)) USING (comment_id) WHERE c.comment_id = 9876;</span></p><p><span class="Apple-style-span">==========</span></p><p><span class="Apple-style-span">SQL Antipatterns </span></p><p><span class="Apple-style-span">- Highlight Loc. 2233-36 | Added on Sunday, August 08, 2010, 07:16 PM</span></p><p><span class="Apple-style-span" style="font-family: 'courier new'; font-size: x-small; ">IEEE 754 represents floating-point numbers in a base-2 format. The values that require infinite precision in binary are different values from those that behave this way in decimal. Some values that only need finite precision in decimal, for instance 59.95, require infinite precision to be represented exactly in binary. The FLOAT data type can't do this, so it uses the closest value in base-2 it can store, which is equal to 59.950000762939 in base-10.</span></p><p><span class="Apple-style-span"><br /></span></p><p><span class="Apple-style-span">==========</span></p><p><span class="Apple-style-span">SQL Antipatterns </span></p><p><span class="Apple-style-span">- Highlight Loc. 4777-78 | Added on Monday, August 30, 2010, 07:48 AM</span></p><p><span class="Apple-style-span"><br /></span></p><p><span class="Apple-style-span">Mitch Ratcliffe said, "A computer lets you make more mistakes faster than any other human invention in human history…with the possible exception of handguns and tequila."</span></p><p><span class="Apple-style-span">==========</span></p><p><span class="Apple-style-span">SQL Antipatterns </span></p><p><span class="Apple-style-span">- Highlight Loc. 4822-26 | Added on Monday, August 30, 2010, 07:55 AM</span></p><p><span class="Apple-style-span" style="font-family: 'courier new'; font-size: x-small; ">But do the math: if you generate unique primary key values as you insert 1,000 rows per second, 24 hours per day, you can continue for 136 years before you use all values in an unsigned 32-bit integer. If that doesn't meet your needs, then use a 64-bit integer. Now you can use 1 million integers per second continuously for 584,542 years. It's very unlikely that you will run out of integers!</span></p><p><span class="Apple-style-span">==========</span></p><p><span class="Apple-style-span">SQL Antipatterns </span></p><p><span class="Apple-style-span">- Highlight Loc. 5037-40 | Added on Monday, August 30, 2010, 10:56 PM</span></p><p><span class="Apple-style-span" style="font-family: 'courier new'; font-size: x-small; ">Even among developers who accept best practices when developing application code, there's a tendency to think of database code as exempt from these practices. I call this antipattern Diplomatic Immunity because it assumes that the rules of application development don't apply to database development.</span></p><p><span class="Apple-style-span">==========</span></p><p><span class="Apple-style-span">SQL Antipatterns </span></p><p><span class="Apple-style-span">- Highlight Loc. 5537-38 | Added on Wednesday, September 01, 2010, 07:14 AM</span></p><p><span class="Apple-style-span" style="font-family: 'courier new'; font-size: x-small; ">Young man, in mathematics you don't understand things. You just get used to them. John von Neumann</span></p><p><span class="Apple-style-span">==========</span></p><p><span class="Apple-style-span">SQL Antipatterns </span></p><p><span class="Apple-style-span">- Highlight Loc. 5549-51 | Added on Wednesday, September 01, 2010, 07:16 AM</span></p><p><span class="Apple-style-span" style="font-family: 'courier new'; font-size: x-small; ">This term relational doesn't refer to relationships between tables. It refers to the table itself, or rather, the relationship between columns within a table. In a way, it refers to both.</span></p><p><br /></p><p></p>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-81242358475997983742010-07-26T21:11:00.006-05:002010-07-26T21:47:02.434-05:00¿ocupa o no el indice?Este es uno de los blogs que me gusta seguir <a href="http://www.depesz.com/"> http://www.depesz.com/</a> habla sobre todo de queries sql y esas cosas, pero todo basado en postgres. Me gusta seguirlo y hacer lo posible por imitar sus problemas pero en db2, he aqui un intento:<a href="http://www.depesz.com/index.php/2010/07/25/how-to-order-by-some-random-query-defined-values/">HOW TO ORDER BY SOME RANDOM – QUERY DEFINED – VALUES?</a><br /><br />Esta funcion viene por defecto en postgres y se ocupa demasiado, te da muchas ventajas no la eh optimizado ni nada, y por ahi pienso que se puede usar metaprogramación para que quede más rápida, pero ahí va.<br /><br /><pre name="code" class="sql">CREATE OR REPLACE FUNCTION generate_series (ini integer, fin integer, inc integer)<br />RETURNS TABLE (val integer)<br />LANGUAGE SQL<br />READS SQL DATA<br />NO EXTERNAL ACTION<br />DETERMINISTIC<br />RETURN<br /> with dummy(id) as (<br /> select generate_series.ini from sysibm.sysdummy1<br /> union all<br /> SELECT id + generate_series.inc FROM dummy WHERE id < generate_series.fin<br /> )<br /> select id from dummy@ </pre><br />Las pruebas de rigor<br /><br /><pre name="code" class="sql">select * from TABLE(generate_series(1,20,2)) as X@<br />--SELECT substr(funcschema, 1, 12) as funcs, substr(funcname, 1, 30) as func from syscat.functions ORDER BY 1,2 @<br /></pre><br />Empezamos con el tutorial.<br /><br /><pre name="code" class="sql">CREATE TABLE test_data (<br />id INT NOT NULL PRIMARY KEY, --te pide afuerza el not null<br />codename VARCHAR(254)<br />)@<br /><br />SELECT * FROM test_data @<br /><br />INSERT INTO test_data (id, codename)<br />SELECT i.val, 'codename for:' || i.val<br />FROM TABLE(generate_series(1,100,1)) as i @ -- pequeño truco, hay que poner TABLE si no no jala<br /></pre><br />Y este es el query que nos interesa, muy sencillo, pero me trae sus consecuencias.<br /><br /><pre name="code" class="sql">SELECT id, cast(codename as varchar(15)) FROM test_data WHERE id IN (3, 71, 5, 16);@<br />ID 2 <br />----------- ---------------<br />3 codename for:3<br />71 codename for:71<br />5 codename for:5<br />16 codename for:16<br />* 4 row(s) fetched, 4 row(s) output.<br />* Elapsed Time is: 0.001058 seconds<br /></pre><br />Y sorpresa, los valores nos los da ya ordenados. No hay que hacer más trucos, pero a mi experiencia esto esta mal.<br /><br /><pre name="code" class="sql"><br />SELECT id, cast(codename as varchar(15)) FROM test_data WHERE id IN (71, 3, 5, 16);@<br />ID 2 <br />----------- ---------------<br />71 codename for:71<br />3 codename for:3<br />5 codename for:5<br />16 codename for:16<br />* 4 row(s) fetched, 4 row(s) output.<br /></pre><br />Pequeñas variaciones y hace lo mismo. Ahora bien, ¿esto esta bien? segun yo no, los datos deberian de aparecer de acuerdo al orden de inserción, si no abría que asumir que la db esta haciendo algo que no le hemos indicado, en este caso ordenar. Pero el error sigue siendo otro, en esta consulta no se ocupa el indice<br /><table style="width:auto;"><tbody><tr><td><a href="http://picasaweb.google.com/lh/photo/D3TcZ3J5I7MGAbGvkSw4uFgtQzbnaQKkpzGJ2H8lOAE?feat=embedwebsite"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkQIzm6AWFMM0SrA8MRCgPpnInei6ZBxynQogdj0bQRADKFGyojx4nYvNj2umY8vLTzfH-76kNZ4c1eP3aJFDVhSOLJOHieTVbOVfXMVnuJyCcZCQPrL1u4GuBM35IOlS_e2qn4A/s144/sin%20indice.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:right">De <a href="http://picasaweb.google.com/hgmiguel/MiguelAngelHuertaGonzalez?authkey=Gv1sRgCJ2w4e-Zt4DEqwE&feat=embedwebsite">Miguel Angel Huerta Gonzalez</a></td></tr></tbody></table><br /><br /><pre name="code" class="sql"><br />SELECT id, cast(codename as varchar(15)) FROM test_data WHERE id IN (71, 3, 5, 16, 27, 19, 2);@<br />ID 2 <br />----------- ---------------<br />2 codename for:2<br />3 codename for:3<br />5 codename for:5<br />16 codename for:16<br />19 codename for:19<br />27 codename for:27<br />71 codename for:71<br />* 7 row(s) fetched, 7 row(s) output.<br />* Elapsed Time is: 0.050610 seconds<br /></pre><br /><br />Y aqui esta lo que esperamos!!!, porque lo hizo? porque hay más datos?, si parece que ocupa el indice solo cuando hay muchos datos. Este es un comportamiento normal, las base de datos solo van al índice cuando hay que ir al índice.<br /><br /><table style="width:auto;"><tbody><tr><td><a href="http://picasaweb.google.com/lh/photo/r-adxtSvOtxPuWmWz4CkolgtQzbnaQKkpzGJ2H8lOAE?feat=embedwebsite"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTwz5oasPjWlh-tnxzDvVNMLAz8LA_4KHYNOBi_etLD5ARy4Pf_bK5UB-G-hVHY_YBkJYzdatwZG5lep9OOH9jgXavgHaPciM8xMOdwxRsYq7Qcty6yaKjjogf1CJ-jCz8aEPqbA/s144/indice.jpg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:right">De <a href="http://picasaweb.google.com/hgmiguel/MiguelAngelHuertaGonzalez?authkey=Gv1sRgCJ2w4e-Zt4DEqwE&feat=embedwebsite">Miguel Angel Huerta Gonzalez</a></td></tr></tbody></table><br /><br />Imaginemos el seq scan, va comparando 1 por 1, y es por esto que los datos nos los da como aparecen en el IN. Cuando va al indice, rearma la consulta y los datos no los trae de acuerdo a como aparecen en el ínce.<br /><br />Realizé otra prueba en sqlite, donde solo inserte 10 datos y ahi lo tienen, los datos aparecen en el orden del indice y no en el de insercion como pensaba...<br /><br /><pre name="code" class="sql"><br />sqlite> SELECT * FROM test_data WHERE id IN (3, 1, 5, 6);<br />1|codename for 1<br />3|codename for 3<br />5|codename for 5<br />6|codename for 6<br />sqlite><br /><br />-- Un select sin nada si nos da por orden de insercion.<br />sqlite> SELECT * FROM test_data ;<br />10|codename for 10<br />9|codename for 9<br />8|codename for 8<br />7|codename for 7<br />6|codename for 6<br />5|codename for 5<br />4|codename for 4<br />3|codename for 3<br />2|codename for 2<br />1|codename for 1<br /><br />-- Mis intentos de sacar un explain en consola no prosperaron<br />-- EXPLAIN PLAN SET QUERYNO = 13<br />-- EXPLAIN PLAN SET QUERYNO = 1 FOR SELECT * FROM test_data WHERE id IN (3, 71, 5, 16);<br /><br /></pre>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-3469504738276789422010-07-22T00:41:00.003-05:002010-07-22T01:01:31.782-05:00Land the tech job you love - crítica<p>Excelente libro, me hubiera encantado leerlo unos cuantos meses antes de tomar la desición de dejar mi trabajo, hay muchas cosas que hay que estar haciendo continuamente, se tenga o no trabajo. Les dejo los consejos que he tomado de este libro:</p><h2>Hacer un CV.</h2><p>Hay que hacerlo, eh irle añadiendo cosas, recomienda usar txt (yo tambien), igual y aprendes VIM o EMACS y ya sería algo nuevo que añadir a tu CV, hay que hacerlo cada 6 meses, si no tienes nada que añadir es un buen indicativo de que te hace falta un cambio y de que te has estancado en tu carrera.</p><p>Puedes ver el <a href="http://petdance.com/resume/">CV del autor </a>y darte una idea de como tener el tuyo</p><h2>Tener un portafolio.</h2><p>Tienes código que te den ganas de imprimir, hazlo y en una entrevista tendrás la oportunidad de mostrarlo. Lo mismo pasa con los diagramas de base de datos, que tal el esquema de la red que administras o planes de proyectos. Todo eso cuenta y te dará ventaja sobre otros candidatos.</p><p> </p><h2>Contactos.</h2><p>Tener una red de contactos a los cuales pedir ayuda, por ejemplo, en mi ultimo puesto estuve trabajando con varios consultores, de los cuales no me quede ninguna referencia, si las tuviera no dudaria en enviarles un mail y preguntar si me pueden recomendar con RH o algo asi. En nuestra carrera nos gusta ayudar porque se tiene que trabajar muchas veces en equipo, estamos acostumbrados a preguntar y responder dudas tecnicas, etc. Asi que no creo que lo tomen a mal si es que pides ayuda de este tipo. Y definitivo no lo tomes a mal si recurren a ti y trata de indicarles la ruta correcta.</p><h2>Buscar siempre tu proximo trabajo:</h2><p>Esto te ayudará a ver como se esta moviendo el mercado, si las tecnologias que dominas estan arcaicas o ya nadie las ocupa, te dará una idea sobre que skills tienes que estudiar y además te darás idea de como se estan moviendo los salarios. Si crees que estas cometiendo algo desleal al buscar trabajo te dejo estas fraces que rescato del libro:</p><p></p><p>"First, your primary loyalty has to be to yourself, not to your organization"</p><p>"Second, I guarantee you that your company has no loyalty to you"</p><p> Haz estas sencillas preguntas, y si te falta algo empieza a trabajar en ello:</p><p></p><ul><li>¿Tu CV esta actualizado?</li><li>¿Tus habilidades siguen siendo competitivas con el resto del mercado?</li><li>¿Tienes al menos 5 personas a las que puedas pedir ayuda para encontrar trabajo?</li><li>¿Tienes al menos 3 personas a las que puedas dar como referencia en un trabajo?</li></ul><div>Por último: Aprender aprender, siempre tratar de aprender algo nuevo y escribir hacerca de ello. Si crees que puedes pasar un examen de certificación, hazlo.</div><div><br /></div><div><br /></div><div>Ficha tecnica:</div><div><a href="http://pragprog.com/titles/algh/land-the-tech-job-you-love">Land the Tech Job You Love</a></div><div><div>by Andy Lester</div><div>280 pages, Jun 2009</div><div>ISBN: 978-1-93435-626-5</div></div><div><br /></div><p> </p><p><br /></p>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com2tag:blogger.com,1999:blog-7890269.post-39319690216315771072010-07-15T23:03:00.004-05:002010-07-16T01:02:27.910-05:00vim and db2<p>Una de las herramientas que más me gusta y la que más me critican es el uso de <a href="http://www.vim.org/">VIM</a>, pero estoy acostumbrado a usar máquinas poco potentes (mi actual máquina es una mini acer aspire one) y el ahorro de memoria es muy apreciado para mi.</p><p>Actualmente me estoy especializando en db2 y como sabrán todas sus herramientas visuales de administración corren en java (malo para la memoria) así que ¿por qué no integrar db2 y vim?. Y como le pasa a mi amigo <a href="http://twitter.com/hoshdog">Toño</a>, con casi todas sus buenas ideas, ya esta inventado. No tan solo se puede conectar a db2, sino que soporta varias bases de datos, el plugin de vim es <a href="http://www.vim.org/scripts/script.php?script_id=356">dbext.vim</a>, después de leer la documentación y seguir el :h db2ext-tutorial, me tiraba el siguiente error:</p><p></p><p></p><blockquote><p>Connection: T(DB2) D(sample) at 22:28</p><p>*** Invalid argument(s) for command line option</p><p>*** For option "-s"</p><p>* Type 'db2batch -h' for help.</p><p>/bin/bash: -f: no se encontró la orden</p><p>To change connection parameters:</p><p>:DBPromptForBufferParameters</p><p>Or</p><p>:DBSetOption user|passwd|dsnname|srvname|dbname|host|port|...=<value></p><p>:DBSetOption user=tiger:passwd=scott</p><p>Last command(rc=127):</p><p>db2batch -q off -s off-d sample -l ; -f /tmp/vZbICSm/dbext.sql</p><p>Last SQL:</p><p> SELECT id FROM customer;</p></blockquote><p></p><p></p><p> </p><p>Grave error, pero hay que fijarnos bien en el comando que ejecuta:</p><p>db2batch -q off -s off-d sample -l ; -f /tmp/vZbICSm/dbext.sql</p><p>Que tiene de malo, varias cosas:</p><p>-s off-d sample :No hay espacio entre off-d</p><p>-l ; :en linux ; tiene un significado especial asi que hay que encerrarlo entre "" o de plano quitarlo</p><p>Leyendo un poco el código fuente del plugin .vim/autoload/dbext.vim, vemos la falla, la cual se localiza en las variables:</p><p></p><p></p><blockquote><p>g:dbext_default_DB2_cmd_options.'':'-q off -s off'</p><p>g:dbext_default_DB2_cmd_terminator.'':';'</p></blockquote><p></p><p>Así que para no aburrirles tanto ejecutamos en vim:</p><p>:let g:dbext_default_DB2_cmd_options = '-q off -s off '</p><p>:let dbext_default_DB2_cmd_terminator = ''</p><p>y ya tendremos funcionado nuestro plugin.</p><p> Ver video con la nostalgia de mis tecladazos!!!</p><p><i>Edit: no se pudo subir el audio...</i></p><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/VX-IEuspxRI&hl=en_US&fs=1"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed src="http://www.youtube.com/v/VX-IEuspxRI&hl=en_US&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-6647957515010393422010-07-06T11:21:00.002-05:002010-07-06T12:00:51.522-05:00Error CLI0622EAl momento de instalar DB2 express - C no podía acceder a las herramientas gráficas, como el command center (db2cc), debido al error cli0622e:<div><div><br /></div><div>db2inst1@macuile:~$ <b>db2 ? CLI0622E</b></div><div><br /></div><div></div></div><div><div></div></div><blockquote><div><div>CLI0622E Error accessing JDBC administration service extensions.</div></div><div></div></blockquote><div><br /></div><div>Casi toda la ayuda en google dice que el error es debido a que no tenemos los driver JDBC en nuestro directorio $inst/sqllib, pero en mi caso si estaban todos.</div><div></div><div><br /></div><div>El problema huele a cosa de java, y lo es:</div><div><div><br /></div><div>db2inst1@macuile:~$ <b>db2 get dbm cfg | grep JDK_PATH</b></div><div> <blockquote>Java Development Kit installation path (JDK_PATH) = /usr/lib/jvm/java-6-sun/</blockquote></div><div><br /></div></div><div>No recuerdo cual era mi JDK_PATH por defecto, pero tampoco funcionaba con eso</div><div>¿por que?</div><div>Supongo que porque debian no está soportado tal cual en la instalación, se quedan muchas rutas por defectos y alguno que otro problema concerniente a esta lucha entre las distribuciones. Además de que no todas las implementaciones del JRE son iguales, algo habrá hecho IBM con es jdk</div><div><br /></div><div>La solucíon, encontrar el JRE que instala DB2, en mi caso </div><div> /home/ibm/db2/V9.7/java/jdk32/</div><div><br /></div><div>Ejecutar el siguiente comando:</div><div><div>$ <b>db2 update dbm cfg using JDK_PATH /home/ibm/db2/V9.7/java/jdk32/</b></div></div><div><br /></div><div>Esto tendría que hacerse por instancia.</div><div><br /></div><div>Datos:</div><div><div>db2inst1@macuile:~$ <b>db2level</b> </div><div></div></div><blockquote><div><div>DB21085I Instance "db2inst1" uses "32" bits and DB2 code release "SQL09071" </div><div>with level identifier "08020107".</div><div>Informational tokens are "DB2 v9.7.0.1", "s091114", "IP23033", and Fix Pack </div><div>"1".</div><div>Product is installed at "/home/ibm/db2/V9.7".</div></div><div></div></blockquote><div><br /></div><div><div>miguel@macuile:~/Desktop$ <b>uname -a</b></div><div><blockquote>Linux macuile 2.6.32-3-686 #1 SMP Thu Feb 25 06:14:20 UTC 2010 i686 GNU/Linux</blockquote></div></div><div><br /></div><div><div>miguel@macuile:~/Desktop$ <b>cat /etc/debian_version</b> </div><div><blockquote>squeeze/sid</blockquote></div></div><div><br /></div><div><br /></div>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com2tag:blogger.com,1999:blog-7890269.post-70191945709433002452010-02-20T22:44:00.001-06:002010-02-20T22:44:02.993-06:00Sobre mi profesión<h1>¿Cuando dejo de gustarme mi profesión?</h1><br><div>Precisamente el título lo dice y mi <b>respuesta </b>es: cuando me di cuanta de que <b>hacía más de profesión que de ingeniería</b>. Y es que todos cuando somos niños queremos <b>salvar al mundo</b>, pero, cuando vamos creciendo, poco a poco esta ilusión se desidealiza y nos muestra la realidad, lo difícil que resulta salvar a un mundo dado a la perdición.</div><br><div>Y entonces uno quiere <b>solo un momento</b>, solo un instante, solo un grano, algo que aporte al mundo. Siempre eh pensado que nuestra carrera es demasiado joven, a lo más 100 años, esto nos da la oportunidad del descubrimiento, de la <b>verificación y corrección</b>, de los <b>teoremas</b>, etc. Es triste (o intrigante) leer pappers como los de Dijkstra, o de Thompson, etc, etc, etc, escritos hace unos cuantos años y que sigan teniendo validez, como es posible que aquellas teorías se pueda verificar hoy, y de ahí nada más. Existe algo que nos detiene y que nos estanca en esta era, cuanta ciencia ficción ya nos ha revasado, no hay maquinas inteligentes, no hay autos voladores, no podemos dar ordenes con el cerebro, en fin. Muchos vieron el potencial y pocos han visto que no lo hemos alcanzado. Tal vez soy un poco exigente al no dar mérito a cosas como la usabilidad, el testing, etc, será porque no es mi área de aplicación o tal vez porque veo que pocos lo ocupan.</div><br><h2><b>Sobre el trabajo</b></h2>Hay un <a href="http://hgmiguel.blogspot.com/2009/01/el-arte-de-trabajar.html" id="lh8." title="pensamiento">pensamiento</a> que ha cambiado mi estilo de vida y forma de pensar, eso fue hace un año más o menos, hace un año más o menos que pienso en renunciar y buscar aquella perfección, pero, en ese entonces se dio la oportunidad de dejar de programar y empezar a administrar sistemas y base de datos, y precisamente ya me volvi a aburrir, ya me llego el diarismo, aunque administro varias plataformas, ya no veo nada nuevo, siempre es repetir cosas, siempre es sobre el trabajo. Esta es la razón principal de mi renuncia (a hacerse efectiva el 31 de marzo).<br><br><div>P.D. Son solo palabras tiradas al azar. Yo también veo que son ilógicas las unas comparadas con las otras.<br><br><br><br><br></div><br>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com1tag:blogger.com,1999:blog-7890269.post-12240873315779934512009-11-11T23:33:00.001-06:002009-11-11T23:33:02.486-06:00Observar (Watch) un directorio con python<h1>Sobre python y directorios</h1><div>Pues una de las actividades de mi trabajo (aparte de estar alerta al <a id="tw6w" href="http://www.f5.com/products/big-ip/" title="foco rojo">foco rojo</a>), es transferir archivos de un sitio a otro y dentro del otro a otro, ¿que interesante no? Pues para hacerlo más interesante se me ocurrió automatizar todo el proceso.</div><div><br></div><div>Lo que hago es observar un directorio por si hay cambios (actualizaciones, borrados, nuevos archivos), si los hay los envio al servidor, en este primer ambiente tengo <a id="rlmz" href="http://python.org/" title="python">python</a> instalado asi que encontré un <a id="d8xi" href="http://tgolden.sc.sabren.com/python/win32_how_do_i/watch_directory_for_changes.html" title="codigo">codigo</a> y le realice algunos cambios. Fueron pequeños cambios solo para notar los archivos que fueron modificados recientemente.</div><div><br></div><div>import os, glob, time</div><div>from os.path import join, getsize, basename</div><div><br></div><div>path_to_watch = r'D:\Documentos\bi\test'</div><div><br></div><div>before = dict ([(f, os.stat(join(path_to_watch,f)).st_mtime) for f in os.listdir (path_to_watch)])</div><div>while 1:</div><div> time.sleep (60)</div><div> after = dict ([(f, os.stat(join(path_to_watch,f)).st_mtime) for f in os.listdir (path_to_watch)])</div><div> added = [f for f in after if not f in before]</div><div> removed = [f for f in before if not f in after]</div><div> different = dict ([(f, None) for f in before if before.get(f) - after.get(f) != 0 ])</div><div> if added: print "Added: ", ", ".join (added)</div><div> if removed: print "Removed: ", ", ".join (removed)</div><div> if different: print "Changed: ", ", ".join(different)</div><div> before = after</div><div><br></div><div>Como me impresiona la simpleza del código python, la forma en que uno lo lee es muy interesante.</div><div><br></div><div>Ahora en el otro ambiente no tenia python, asi que pues ha googlear un poco y nos encontramos con <a id="gtsf" href="http://www.py2exe.org/" title="py2exe">py2exe</a>, y en el ambiente hostil pues ya esta funcionando a la perfección y sin haber instalado python allí</div><div><br></div><div><div></div><div><div>Casi todo fue sacado de aqui:</div><div><a href="http://tgolden.sc.sabren.com/python/win32_how_do_i/watch_directory_for_changes.html">http://tgolden.sc.sabren.com/python/win32_how_do_i/watch_directory_for_changes.html</a></div></div></div><br><h1>Sobre el trabajo en oficina</h1><div>A veces me pregunto para que voy a la oficina, todo lo que hago lo puedo realizar desde mi depto, algunos sostienen que por las relaciones laborales, otros que pues uno se aburriría, pero al menos en mi caso y viviendo a 15 min de mi trabajo, pudiera ir a comer con los compañeros unas cuantas veces a la semana, ahí si entraría al equipo de fútbol, tengo que ir a fuerza a mis clases de ingles 3 veces a la semana, en fin. </div><div><br></div><div>Aparte de que por alguna extraña razón me incomoda estar allí, digo, pareciera que no trabajo, pero en realidad el trabajo de un sysadmin no requiere 100% de atención, uno acaba sus monitoreos, realiza su documentación, se testean mejoras a los appliances y hasta ahí. En cambio, un programador pues tiene que estar tirando lineas o investigando como tirarlas, alguien de operación tiene que ver que el flujo del negocio valla bien, arreglar reclamaciones, en fin, es un tipo diferente de trabajo.</div><div><br></div><div><br></div><br>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0tag:blogger.com,1999:blog-7890269.post-87446151256808300402009-11-10T23:32:00.001-06:002009-11-10T23:32:38.271-06:00stopmotion con gstreamer<h1>STOPMOTION o sobre energía solar y otros temas</h1><div><br></div><div>He estado tratando de encontrar la mejor ubicacion para un <a id="i15r" href="http://www.gstriatum.com/energiasolar/blog/2008/03/18/calentador-solar-de-aire-casero/" title="experimento solar">experimento solar</a>, por supuesto que necesito que le de directamente el sol por mucho tiempo, asi que me di a la tarea de tomar una foto con la camara de mi acer aspire one cada 10 min. en lugares estrategicos de mi depto. </div><div><br></div><div>Para esto utilice un script como el que sigue:</div><div><br></div><div>#!/usr/bin/ksh</div><div>while [ 1 == 1 ]; do </div><div> gst-launch-0.10 v4l2src ! ffmpegcolorspace ! pngenc ! filesink location=foo$X.png; </div><div> let X+=1; </div><div> sleep 30; </div><div>done</div><div><div><br></div><div>Esto generara archivo foo0.png, foo1.png, foo2.png, ..., foon.png luego con el programa <a id="nw_j" href="http://stopmotion.bjoernen.com/" title="stopmotion">stopmotion</a> las podemos unir no dará un buen efecto...</div><div><br></div><div>Para instalar stopmotion en<a id="lw3r" href="http://www.debian.org" title="debian">debian</a>:</div><div>$ sudo aptitude install stopmotion</div><div><br></div><div>Como conclusión puedo decir que mi experimento fallo rotundamente, encontré un buen lugar, recibe casi 4 hrs de sol directas, pero necesito demasiado sol para poder calentar mi cuarto, seguiré investigando formas de calentar el ambiente sin consumir tanta electricidad. El gran problema de la calefacción es que se pierde demasiada energía con las corrientes de aire, sin embargo, necesitamos de estas para una buena circulación de aire, si no se empieza a enrarecer el ambiente.</div><div><br></div><h2>Sobre la creatividad</h2><div>Un día divagando con algún conocido se llego a lo conclusión de que grandes artistas y músicos habían compuesto sus obras cumbres cuando estuvieron casados y tuvieron hijos, algo en lo que yo no creía. Así que me di a la tarea de "medio" (me faltaron los hijos) comprobarlo, y si, hay algo, un tipo de motivación que no tiene nada que ver con el arte ni con la música, en general, al menos en este corto tiempo (en el que tuve novia) fui más creativo, me aburrí menos en el trabajo y realicé muchas actividades, que sin esa motivación no las hubiera hecho.</div><div><br></div><div>Pero esa creatividad, ese algo lo descubrimos en otra gran conversación, de esas que se dan muy rara vez, en la que todo lo que nos pasa nos remonta al buen amigo <a id="ix35" href="http://es.wikipedia.org/wiki/Pirámide_de_Maslow" title="Maslow">Maslow</a>, he ahí del porque de la creatividad...</div><div><br></div></div><br>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com1tag:blogger.com,1999:blog-7890269.post-17762015630787201962009-10-30T12:55:00.001-06:002009-10-30T12:55:18.811-06:00F5 y monitoreo de poolsSi tienes un <a id="tj5c" href="http://www.f5.com/" title="F5">F5</a> y quieres hacer funcionar la siguiente <a id="v0qf" href="http://devcentral.f5.com/wiki/default.aspx/iRules/Pool_Member_Status_Page_on_a_Virtual_Server.html" title="iRule">iRule</a> hay que hacer lo siguiente:<br /><div><br></div><div><span style="font-family: Tahoma, Arial, Helvetica; color: rgb(48, 48, 48)"><font face="Courier New"><font size="2">b pool all member | grep -ie "+-> POOL MEMBER" | awk '{print "\""$4"\","}' | sort >/var/class/pool_member_status_list.class <br /></font></font><div><font class="Apple-style-span" color="#000000" face="Verdana"><br></font></div></span></div><div>Y con esto debe de funcionar la iRule y ya podremos monitorear el estatus de nuestros pools members.</div><div><br></div><div>Mi version es:</div><div> BIG-IP 9.4.5 Build 1049.10 Final</div><div><div><br></div></div><br><br><br>Anonymoushttp://www.blogger.com/profile/03821827622952340036noreply@blogger.com0