<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4898013526528408057</id><updated>2011-10-01T16:52:57.987+02:00</updated><category term='profesion'/><category term='CMS Open Source'/><category term='trabajo opinión'/><category term='desarrollo'/><category term='lucene busquedas compass java'/><category term='maven java'/><category term='java enumeration'/><category term='Java'/><category term='java serializable TreeSet'/><category term='Groovy'/><category term='opinion guru'/><category term='java.net.Uri.resolve path relativo HTML java'/><title type='text'>Los puntos sobre las Jotas</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-2730970861098944059</id><published>2011-09-15T14:25:00.000+02:00</published><updated>2011-09-15T14:27:35.788+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lucene busquedas compass java'/><title type='text'>Lucene: sin regla ni Compass</title><content type='html'>&lt;div class="MsoNormal"&gt;Saludos después de una larga pausa donde, básicamente, me he dedicado a la familia y al ocio, para que negarlo. La verdad es que no estaba seguro de si volvería a escribir o no en el blog, ya que mi visión de eso llamado comunidad se va degradando con el tiempo, pero los ánimos me han dado para algunas entradas más, y aquí estamos de nuevo.&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="MsoNormal"&gt;Esta entrada debería haber sido algo diferente, ya que mi intención era principalmente comentar la librería &lt;a href="http://www.compass-project.org/"&gt;Compass&lt;/a&gt;, que es una especie de envoltorio de &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt; para facilitar su uso cuando tratamos con objetos &lt;a href="http://www.oracle.com/us/technologies/java/"&gt;Java&lt;/a&gt;, pero después de usarla en unos cuantos proyectos y estar relativamente satisfecho con su uso, un ligero problemilla me hizo consultar sus listas de distribución donde buceando entre el spam me encontré con la sorpresa de que su autor &lt;a href="http://www.kimchy.org/the_future_of_compass/"&gt;declaraba oficialmente&lt;/a&gt; que pasaba a hacer otra cosa y que ya no le interesaba trabajar en la librería. Al ser un one-man project, la cosa queda en que &lt;a href="http://www.compass-project.org/"&gt;Compass&lt;/a&gt; es, en estos momentos, “&lt;a href="http://es.wikipedia.org/wiki/Abandonware"&gt;abandonware&lt;/a&gt;”, por lo que hacer entradas explicando su uso, ventajas… me parece hacerle un flaco favor al pobre programador que debido a ellas acabe usándola y encontrándose la misma desagradable sorpresa que yo.&lt;o:p&gt;&lt;/o:p&gt;&lt;/div&gt;&lt;div class="MsoNormal"&gt;Sin embargo, aparte de advertir del estado del &lt;a href="http://www.compass-project.org/"&gt;Compass&lt;/a&gt;, he decido modificar el contenido de la entrada y limitarme a hablar de los conceptos que he aprendido últimamente del &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt; y que son aplicables fuera de &lt;a href="http://www.compass-project.org/"&gt;Compass&lt;/a&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="MsoNormal"&gt;Así que si alguna vez te toca trabajar con Lucene, espero que estos consejillos te sirvan:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Lo más normal, si estas realizando búsquedas en castellano o algún lenguaje con caracteres "raros" para los anglos, es que quieras usar un analizador personalizado (custom analyzer) que utilice, al menos, los filtros ISOLatin1AccentFilter, LowerCaseFilter y StandardFilter. De esta forma tus documentos (así es como llama &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt; a lo que sea que indexes) se indexarán sin tener en cuenta mayúsculas, ni acentos u otros caracteres no-ascii.&lt;/li&gt;&lt;li&gt;Eso sí, una vez indexados los documentos de esa forma, ojo con una característica del &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt; muy desagradable: Las búsquedas se deben pasar, habitualmente, por el mismo analizador que al indexar, pero el Lucene ignora al analizador sin decir nada si en las búsquedas se usan comodines (‘*’ o ‘?’).&amp;nbsp;Así que si realizamos una búsqueda sin comodines, los resultados no serán independientes de las mayúsculas o los acentos y nos llevaremos desagradables sorpresas. La excusa oficial, con su pequeña parte lógica, es que el analizador puede cambiar los términos de búsqueda, por ejemplo para buscar &amp;nbsp;en singular y plural indistintamente, y entonces al hacer esos cambios junto a comodines, el termino final de búsqueda podría no parecerse a lo que quería el usuario. Bueno, vale. ¡Pero déjame elegir! En nuestro caso no puede ocurrir nada de eso, simplemente cambiamos letras mayúsculas por minúsculas y cambiamos á por a … pero Lucene considera que dejar escoger al programador, pobre tonto, es un peligro y no da la opción de desactivar esa “ayudita”. Sin comentarios. La única solución que he encontrado es pasar por esos mismo filtros los términos de búsqueda “manualmente” y &lt;b&gt;antes&lt;/b&gt; de pasárselos al &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt;, lo cual me parece una chapuza. Pero avisados&amp;nbsp;quedáis.&lt;/li&gt;&lt;li&gt;Otro truco útil es para cuando la gente quiere hacer una búsqueda de palabras que “acaben en”, o sea *algo . &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt; está pensado para búsquedas que “empiecen por”, o sea algo*, y en cambio empezar por un comodín es muy ineficiente. ¿Como solventarlo? Muy sencillo, a la hora de indexar, indexamos ese campo al revés y a la hora de buscar le damos la vuelta al termino de búsqueda, o sea ogla*. Por el módico precio de tener indexado el campo dos veces, volvemos a tener búsquedas eficientes: voilà.&lt;/li&gt;&lt;li&gt;Este último caso es un claro ejemplo de una técnica general muy útil cuando trabajamos con búsquedas indexadas, lo que hacemos con &lt;a href="http://lucene.apache.org/"&gt;Lucene&lt;/a&gt;, vaya: En caso de tener problemas al montar el criterio de búsqueda, se puede probar a modificar la forma en que se indexa el contenido para facilitar las cosas. Por ejemplo, tenemos una lista de productos que pueden venderse en distintas tiendas y de esas tiendas hay sucursales en distintas provincias. Si a la hora de indexar, usamos un campo para indexar la lista de tiendas en las que se vende un producto y en otro campo la lista de provincias donde se vende… ¿Cómo podemos saber en qué provincias se vende el producto X en una tienda determinada? La respuesta es que así indexado no lo podemos saber, ya que hemos perdido la relación tienda-provincia. En este caso lo que tenemos que hacer es crear un nuevo campo donde indexemos el par tienda-provincia, que es por el que buscaremos para este tipo de consultas.&lt;/li&gt;&lt;li&gt;De igual forma, podemos indexar en varios campos los apellidos junto con el nombre o los apellidos por separado, según como queramos poder buscarlos. A no ser que tengamos un índice de modificaciones muy alto, o un volumen de datos inmenso, no hay que tener miedo en invertir en la indexación a cambio de obtener un rendimiento mucho más alto en la parte presumiblemente más usada: las búsquedas.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-2730970861098944059?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/2730970861098944059/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2011/09/lucene-sin-regla-ni-compass.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2730970861098944059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2730970861098944059'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2011/09/lucene-sin-regla-ni-compass.html' title='Lucene: sin regla ni Compass'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-3861890683171882943</id><published>2011-07-02T16:56:00.000+02:00</published><updated>2011-07-02T16:56:14.385+02:00</updated><title type='text'>Generando números de versión automáticamente con Maven</title><content type='html'>En esta entrada de hoy mostraré como implementar un sistema para que los usuarios de nuestras librerías puedan averiguar fácilmente cual es la versión que están utilizando y que se mantenga automáticamente al generar nuestros .jar con Maven.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Introducción&lt;/b&gt;&lt;br /&gt;Uno de los “problemas/features” cuando uno usa &lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt;/&lt;a href="http://ant.apache.org/ivy/"&gt;Ivy&lt;/a&gt;/&lt;a href="http://groovy.codehaus.org/Grape"&gt;Grape &lt;/a&gt;o sistemas similares para gestionar sus dependencias es que independientemente de la versión, las librerías acaban teniendo un nombre común. Es decir, que la librería &lt;i&gt;org.hibernate:hibernate:3.2.6.ga&lt;/i&gt;  acaba llamandose &lt;i&gt;hibernate.jar&lt;/i&gt;, igual que si fuera la &lt;i&gt;3.3.0.SP1&lt;/i&gt; o cualquier otra versión. Esta característica ayuda a la hora de reemplazar una versión con otra, pero dificulta averiguar de un vistazo que versiones de librería estamos utilizando.&lt;br /&gt;&lt;br /&gt;En caso de que nosotros hagamos una librería y se use a través de uno de estos sistemas, nuestros usuarios tendrán el mismo problema para averiguar en el sistema final qué versión está desplegada. Como somos unos “grandes” programadores, queremos facilitar a nuestros usuarios el averiguar está información, pero como somos unos programadores &lt;strike&gt;vagos&lt;/strike&gt;&amp;nbsp;eficientes no queremos tener que hacerlo a mano cada vez que generamos un .jar, y menos si usamos algo como&amp;nbsp;&lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt;&amp;nbsp;para gestionar nuestro proyecto. Así pues, ¿como lo podemos hacer?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Implementación&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Paso 1&lt;/b&gt;&lt;span style="font-weight: normal;"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: normal;"&gt;Lo primero que tenemos que hacer es almacenar el número de versión de forma automática en algún sitio. Un buen sitio para hacerlo es en el fichero Manifest de nuestro .jar, y para hacerlo de forma automática podemos utilizar el plugin de Maven &lt;/span&gt;&lt;b&gt;&lt;a href="http://org.codehaus.mojo.buildnumber-maven-plugin/"&gt;org.codehaus.mojo.buildnumber-maven-plugin&lt;/a&gt;&lt;/b&gt;&lt;span style="font-weight: normal;"&gt;. La documentación que tiene no es demasiado extensa, pero investigando un poco podemos averiguar cómo usarlo. En mi caso, dado que utilizo Mercurial como sistema de versiones y el numero de versión que utiliza no es muy significativo (no es un número correlativo) así que lo he sustituido por una marca de tiempo que me da una indicación más fiable. Si usas algo como &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt;, entonces quizá la configuración estándar del plugin ya te sirva Dado que el plugin no se encuentra en el repositorio central de&amp;nbsp;&lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt;, tenemos que añadir un repositorio de plugins para encontrarlo, de la siguiente forma:&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush: xml"&gt;    &lt;pluginRepository&gt;&lt;br /&gt;        &lt;id&gt;codehaus-snapshot&lt;/id&gt;&lt;br /&gt;        &lt;name&gt;Cohehaus snapshot repository&lt;/name&gt;&lt;br /&gt;        &lt;url&gt;https://nexus.codehaus.org/content/groups/snapshots-group&lt;/url&gt;&lt;br /&gt;        &lt;snapshots&gt;&lt;br /&gt;          &lt;enabled&gt;true&lt;/enabled&gt;&lt;br /&gt;        &lt;/snapshots&gt;&lt;br /&gt;    &lt;/pluginRepository&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Una vez hecho esto, configuramos el plugin para que nos defina unas variables con la información que queremos, así:&lt;br /&gt;&lt;pre class="brush: xml"&gt;    &lt;plugin&gt;&lt;br /&gt;      &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;&lt;br /&gt;      &lt;artifactId&gt;buildnumber-maven-plugin&lt;/artifactId&gt;&lt;br /&gt;      &lt;version&gt;1.0-beta-5-SNAPSHOT&lt;/version&gt;&lt;br /&gt;      &lt;executions&gt;&lt;br /&gt;        &lt;execution&gt;&lt;br /&gt;          &lt;phase&gt;validate&lt;/phase&gt;        &lt;br /&gt;          &lt;goals&gt;&lt;br /&gt;            &lt;goal&gt;create&lt;/goal&gt;&lt;br /&gt;          &lt;/goals&gt;&lt;br /&gt;        &lt;/execution&gt;&lt;br /&gt;      &lt;/executions&gt;&lt;br /&gt;      &lt;configuration&gt;&lt;br /&gt;        &lt;doCheck&gt;true&lt;/doCheck&gt;&lt;br /&gt;        &lt;doUpdate&gt;true&lt;/doUpdate&gt;&lt;br /&gt;        &lt;format&gt;{0,date,dd/MM/yyyy HH:mm:ss}&lt;/format&gt;&lt;br /&gt;        &lt;items&gt;&lt;br /&gt;          &lt;item&gt;timestamp&lt;/item&gt;&lt;br /&gt;        &lt;/items&gt;&lt;br /&gt;      &lt;/configuration&gt;&lt;br /&gt;    &lt;/plugin&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Con esto le decimos que nos añada el timestamp a la variable &lt;i&gt;buildNumber&lt;/i&gt;, que es donde el plugin pone su información.&lt;br /&gt;Por último, configuramos el &lt;b&gt;plugin org.apache.maven.plugins.maven-jar-plugin&lt;/b&gt; para que use esa información para añadir una nueva entrada en el Manifest de nuestro jar. De la siguiente forma:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml"&gt;&lt;plugin&gt;&lt;br /&gt; &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;&lt;br /&gt; &lt;artifactId&gt;maven-jar-plugin&lt;/artifactId&gt;&lt;br /&gt; &lt;version&gt;2.3.1&lt;/version&gt;      &lt;br /&gt; &lt;configuration&gt;&lt;br /&gt;  &lt;archive&gt;&lt;br /&gt;   &lt;addMavenDescriptor&gt;false&lt;/addMavenDescriptor&gt;&lt;br /&gt;   &lt;manifestEntries&gt;&lt;br /&gt;    &lt;MyLibrary-version&gt;${version}-${buildNumber}&lt;/MyLibrary-version&gt;&lt;br /&gt;    &lt;Main-Class&gt;my.package.MainApp&lt;/Main-Class&gt;&lt;br /&gt;   &lt;/manifestEntries&gt;&lt;br /&gt;  &lt;/archive&gt;&lt;br /&gt; &lt;/configuration&gt;&lt;br /&gt;&lt;/plugin&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Una vez hecho esto, si ejecutamos &lt;i&gt;mvn package&lt;/i&gt;, por ejemplo, para generar nuestro fichero ,jar y miramos el fichero MANIFEST.MF dentro del directorio META-INF, deberíamos ver algo tal que así:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;Manifest-Version: 1.0&lt;br /&gt;Archiver-Version: Plexus Archiver&lt;br /&gt;Created-By: Apache Maven&lt;br /&gt;Built-By: usuario&lt;br /&gt;Build-Jdk: 1.6.0_21&lt;br /&gt;Main-Class:  my.package.MainApp&lt;br /&gt;MyLibrary-version: 0.1-SNAPSHOT-25/06/2011 13:33:01&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Paso 2&lt;/b&gt;:&lt;br /&gt;Bien, ya tenemos almacenada la versión en el .jar. ¿Ahora que hacemos para facilitar que el usuario pueda ver esa información sin tener que abrir el .jar y mirar el manifest? Muy sencillo: la clase my.package.MainApp que es la principal de nuestra aplicación tiene que mostrar esa versión. En mi caso, la librería es una utilidad que no se lanza en linea de comandos, se usa añadiéndola al classpath, así que mi clase MainApp simplemente muestra la versión de la librería.&lt;br /&gt;&lt;br /&gt;¿Y como hacemos para leer el manifest del mismo fichero del cual nos estamos ejecutando? Pues averiguando donde se encuentra el .jar en tiempo de ejecución y accediendo a él para leer el fichero manifest. Podemos hacerlo así (código simplificado sin gestión de errores):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;String jarFileURL = &lt;br /&gt;  MainApp.class.getProtectionDomain().getCodeSource().getLocation().toString();&lt;br /&gt;int pos = jarFileURL.indexOf("!");&lt;br /&gt;if(pos!=-1)&lt;br /&gt;{&lt;br /&gt;  jarFileURL = jarFileURL.substring(0,pos);&lt;br /&gt;}&lt;br /&gt;if(!jarFileURL.startsWith("jar:"))&lt;br /&gt;{&lt;br /&gt;  jarFileURL = "jar:" + jarFileURL;&lt;br /&gt;}&lt;br /&gt;URL manifestUrl = new URL(jarFileURL + "!/META-INF/MANIFEST.MF");&lt;br /&gt;Manifest manifest = new Manifest(manifestUrl.openStream());      &lt;br /&gt;return manifest.getMainAttributes().getValue(“MyLibrary-version”);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Y así podemos hacer que al ejecutar java -jar milibreria.jar nos devuelva la versión de la librería, o si lo ponemos en un método público, podemos usar este número para mostrarlo en las aplicaciones que usen la librería y así puedan saber que versión están utilizando, comprobar si existen nuevas versiones etc.&lt;br /&gt;&lt;br /&gt;Espero que os sirva, al menos a mi me ha servido, y que vuestros usuarios estén más contentos :).&lt;br /&gt;&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-3861890683171882943?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/3861890683171882943/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2011/07/generando-numeros-de-version.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/3861890683171882943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/3861890683171882943'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2011/07/generando-numeros-de-version.html' title='Generando números de versión automáticamente con Maven'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-3642058471352663362</id><published>2011-05-30T21:17:00.000+02:00</published><updated>2011-06-01T14:41:41.306+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='trabajo opinión'/><title type='text'>¿En qué se parece un informático a un limón?</title><content type='html'>En vez de hacer un chiste fácil, y seguramente malísimo, con la frase, voy a tratar de aportar mi grano de arena a la eterna discusión de "¿Por qué el mercado de informáticos está tan mal/desprestigiado y generalmente se cobra tan mal en relación a la valía?"&lt;br /&gt;&lt;br /&gt;Para explicarme debo mencionar la teoría económica de un economista de 1970,&amp;nbsp;&lt;a href="http://es.wikipedia.org/wiki/George_Akerlof" title="George Akerlof"&gt;George Akerlof&lt;/a&gt;, expuesta en un artículo bajo el título&amp;nbsp;"&lt;a href="http://en.wikipedia.org/wiki/The_Market_for_Lemons"&gt;The Market for Lemons: Quality Uncertainty and the Market Mechanism&lt;/a&gt;". En realidad en este caso "&lt;i&gt;lemon&lt;/i&gt;" no se traduciría por limón, de ahí el chiste malo, si no por su acepción menos conocida de "cacharro" o "cosa decepcionante".&lt;br /&gt;Por simplificar, la teoría viene a decir que cuando en un mercado de un artículo, el comprador no tiene información fiable a priori para saber si lo que compra es bueno o es malo, y el vendedor sí, al final en ese mercado se acaban vendiendo solamente los "cacharros", es decir, los artículos de peor calidad y a precios bajos.&lt;br /&gt;Una explicación basada en un ejemplo la podéis encontrar en esta entrada del &lt;a href="http://fermatmargin.blogspot.com/2007/07/market-of-lemons.html"&gt;blog Fermat Margin&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Y eso es en parte lo que nos ocurre en el mercado de los informáticos, especialmente cuando tienen menos experiencia, ya que muchas veces quien contrata no sabe de que va el tema y no tienen experiencia previa, así que no pueden evaluar de forma fiable la valía de los candidatos. &amp;nbsp;Y la cosa va tal que así&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;El comprador (el que contrata) no tiene forma medianamente fiable de saber como le va a salir el artículo (el posible empleado). Para no pasarse, obviamente tira por un sueldo medianero.&lt;/li&gt;&lt;li&gt;Los buenos programadores que merecerían más sueldo, rechazan trabajar por ese sueldo y buscan otras cosas.&lt;/li&gt;&lt;li&gt;Por tanto entre los que aceptan esos trabajos, sólo quedan los que merecen ese sueldo y los que no.&lt;/li&gt;&lt;li&gt;Por tanto el comprador cuando mira lo que obtiene por lo que paga, se da cuenta de que la media le sale por debajo, así que la próxima vez ofrece sueldos más bajos.&lt;/li&gt;&lt;li&gt;Y así recursivamente, hasta que los sueldos son una mierda y en ese mercado sólo trabajan los malos empleados que no podrían aspirar a nada más. Por lo tanto los empleadores ven confirmada su idea de que realmente no ha de pagar más por que todos los que contrata son malísimos, y los buenos empleados no tienen ningún incentivo por hacerlo bien, puesto que les van a pagar como si fueran malos, así que al final se convierten en malos.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Y ahí tenemos un bonito circulo vicioso :). No lo explica todo, y por supuesto los buenos empleados pueden encontrar buenos empleos, si salen de ese mercado, y los empleadores pueden encontrar personal adecuado si no entran en este juego. Pero es una pieza más del puzzle, aparte de la avaricia, la cultura del pelotazo y todos esos tópicos que ya se mencionan suficiente. Simplemente me apetecía añadir este enfoque diferente y curioso que explica algunos de los comportamientos humanos que llevan a situaciones desastrosas.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ni soy economista ni gurú, así que no tengo ni creo tener receta mágica para solucionar este tipo de problemas, pero si crees ser un buen empleado, no te conviertas en un limón y si eres un empleador, no vayas al mercado a por limones. Hay que recordar que la clave de este problema concreto es la información, o falta de ella, de una de las partes. Decir que con más información se soluciona es fácil, aplicarlo en la vida real es lo complicado ;).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-3642058471352663362?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/3642058471352663362/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/11/en-que-se-parece-un-informatico-un.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/3642058471352663362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/3642058471352663362'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/11/en-que-se-parece-un-informatico-un.html' title='¿En qué se parece un informático a un limón?'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-7158217989331415830</id><published>2011-01-01T22:14:00.000+01:00</published><updated>2011-01-01T22:14:00.600+01:00</updated><title type='text'>Estándar de nomenclatura en BDD</title><content type='html'>Hoy una entrada sobre el sistema personal de nomenclatura de BDD que me gusta utilizar en mis aplicaciones cuando tengo la capacidad de decidir. No siempre es así, a veces se trabaja con BDD heredadas o uno no es el responsable de esa área, pero cuando es posible, personalmente prefiero utilizar este sistema que me proporciona la sensación de tener más "ordenada" la BDD.&lt;br /&gt;&lt;br /&gt;La idea, al fin y al cabo, es tener un sistema que sea fácil de seguir y que permita identificar fácilmente los elementos que se estan utiizando en consultas, los que se referencia en mensajes de error, en relaciones entre elementos etc.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resaltar especialmente que este es el sistema que YO utilizo&lt;/b&gt;, pero hay muchos otros cada uno con sus pros y sus contras y &lt;b&gt;no es mi intención decir que es el mejor ni nada remotamente parecido&lt;/b&gt;. En este caso, se cumple el dicho de que para gustos, colores y la organización o el DBA son los responsables de escoger entre la cantidad de opciones disponibles.&lt;br /&gt;&lt;br /&gt;.- El &lt;b&gt;primer punto&lt;/b&gt; es sencillo, escoger &lt;b&gt;tres letras como alias de "la aplicación"&lt;/b&gt; o módulo a desarrollar. Por ejemplo, si vamos a trabajar la parte de almacén y no se ha usado ya ese "álias", podríamos escoger ALM como álias de aplicación.&lt;br /&gt;.- Una vez escogido el primer punto, &lt;b&gt;las tablas empezarían con T + dicho álias, las vistas con V + álias, las restricciones de integridad con K + álias, los índices con I + álias... etc.&lt;/b&gt; Así pues, la tabla para representar los productos en la aplicación sería TALM_PRODUCTO, la de clientes TALM_CLIENTE y así sucesivamente. Los nombres siempre en singular y sin abreviar a no ser que la longitud sea muy muy larga. Hoy en día restringir los nombres a 8 caracteres o menos como se hacía antes tiene muy poco sentido.&lt;br /&gt;.- Dentro de las &lt;b&gt;tablas&lt;/b&gt;, yo prefiero utilizar siempre &lt;b&gt;un identificador único e inventado sin significado de negocio, siempre con el mismo nombre y el mismo tipo&lt;/b&gt;. Además, &lt;b&gt;cada tabla tiene su propio álias y se añade delante de todos los nombres de los campos&lt;/b&gt;. Por ejemplo, la tabla TALM_CLIENTE, con alias CLI, tendria los campos CLI_ID, CLI_NOMBRE, CLI_NIF..., la de productos tendria PRD_ID, PRD_NOMBRE, PRD_CODIGO... Darse cuenta de que PROD_ID sería un identificador inventado y que el número que viene en el producto sería por ejemplo PRD_CODIGO. Existen múltiples discusiones a favor y en contra de usar o no identificadores inventados y no voy a intentar convencer a nadie. Sólo decir que tener que detener la operación del sistema periódicamente para poder anular las restricciones de integridad para solucionar los problemas de errores en los identificadores no inventados da una perspectiva diferente :). No voy a decir que no tenga inconvenientes o que el sistema esté mal hecho si no se sigue. Sólo puedo decir que cuando puedo elegir, YO lo hago así. Siguiendo con lo ya mencionado, la restriccion de &lt;b&gt;clave primaría tiene el alias de la aplicacion y de la tabla seguido del sufijo _PK&lt;/b&gt;, para indicar que es "primary key". Por ejemplo, para la tabla TALM_CLIENTE sería KALM_CLI_PK, para TALM_PRODUCTO sería KALM_PRD_PK etc.&lt;br /&gt;.- Las &lt;b&gt;claves extranjeras simplemente referencian el campo en la tabla "remota" añadiendo el prefijo correspondiente&lt;/b&gt; y sólo en caso de existir más de una clave extranjera contra la misma tabla se añade un sufijo para diferenciarlas. Por ejemplo, en la tabla TALM_STOCK, tendríamos una clave extranjera a TALM_PRODUCTO bajo el nombre STK_PRD_ID, en TALM_FACTURA tendríamos FAC_CLI_ID para referenciar el cliente... etc. Como se puede ver esto facilita enormente la identificación de los elementos de cada relacion, incluso permite automatizar algunas tareas. Por otro lado,&lt;b&gt; las restricciones de integridad de las claves extranjeras indican en este caso ambas tablas y el sufijo _FK&lt;/b&gt;, de "foreign ey". Las restricciones para los ejemplos mencionados serían KALM_STK_PRD_FK y KALM_FAC_CLI_FK. De este modo, al violar una de estas claves, el mensaje de error ya nos indicará claramente cuales con las tablas implicadas.&lt;br /&gt;.- &lt;b&gt;Las restricciones sobre campos indican el nombre del campo y el tipo de restriccion&lt;/b&gt;, por ejemplo PRD_CODIGO sería un campo seguramente único y como tal tendría la restriccion KALM_PRD_CODIGO_UK (de "unique key"), las restricciones de integridad serían igual pero con _CHK al final etc.&lt;br /&gt;&lt;br /&gt;Ese es básicamente el estilo de nomenclatura que sigo y con el que me siento comodo, aunque no soy muy talibán al respecto.&lt;br /&gt;&lt;br /&gt;A la hora de trasladar el modelo de datos a clases Java los prefijos desaparecen y uso un estilo de nomenclatura más "javero" y así de esta forma cada mundo es coherente y sigue sus propias reglas. Eso implica que siempre tengo que especificar los nombres de los elementos que corresponden en cada caso, pero es una "molestia" que tengo automatizada y no me importa pagar ese precio. La razoón para no tener que usar tanto sufijo en Java es que con la estructuración en paquetes y el estilo de los errores ya da suficiente información como para poder situarte rápidamente sin necesitar esas ayudas.&lt;br /&gt;&lt;br /&gt;No intento convencer a nadie de que use nada pero por si a alguien le da alguna idea y/o le sirve, ahí queda dicho. Y una vez dicho esto...¿Usais vosotros, estimados lectores, algún tipo de estilo/estándar de nomenclatura?&lt;br /&gt;&lt;br /&gt;Happy coding! EJ&lt;br /&gt;&lt;br /&gt;PD: Ah, feliz año nuevo ;). A ver si este 2011 nos trata un poco mejor a "la plebe" y un poco peor a los chupasangres del mundo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-7158217989331415830?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/7158217989331415830/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2011/01/estandar-de-nomenclatura-en-bdd.html#comment-form' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/7158217989331415830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/7158217989331415830'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2011/01/estandar-de-nomenclatura-en-bdd.html' title='Estándar de nomenclatura en BDD'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-5540947640092441919</id><published>2010-09-24T19:38:00.000+02:00</published><updated>2010-09-24T19:38:43.288+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java.net.Uri.resolve path relativo HTML java'/><title type='text'>Paths relativos, especificaciones, Java y un bug en "Internetes"</title><content type='html'>&lt;div style="margin-bottom: 0cm;"&gt;Hoy en el trabajo me he encontrado con “un bug en Java e Internetes”. Bueno, más que un bug es un comportamiento extraño por especificaciones obsoletas, implementaciones independientes de contexto etc. etc. Pero es algo que te puedes encontrar en la vida real, así que he pensado que sería interesante contarlo.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;El problema se da al leer un documento HTML con Java y tener que interpretar las direcciones relativas que contiene dicho documento. Si una de las direcciones únicamente contiene un “query string”... ¿Cómo la interpretarías vosotros?&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Es decir, si en la página &lt;a href="http://www.host.com/dir/loquesea.do"&gt;www.host.com/dir/loquesea.do&lt;/a&gt; encontramos un enlace tal que así href=”?param=value”... ¿Dónde ha de ir la página?&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;La respuesta correcta es “depende” :).  &lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;Si estamos hablando de HTML 4.1,  la respuesta es  &lt;a href="http://www.host.com/dir/loquesea.do?param=value"&gt;www.host.com/dir/loquesea.do?param=value&lt;/a&gt;  y si hacéis una prueba con el navegador, lo debería resolver así.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;En cambio... ¿qué ocurre si  procesáis ese documento con Java y usáis  j&lt;a href="http://ava.net.uri/"&gt;ava.net.Uri&lt;/a&gt;.resolve(relativePath) para obtener la nueva URL? El  resultado es &lt;a href="http://www.host.com/dir/?param=value"&gt;www.host.com/dir/?param=value&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;¿Y cual de las dos respuestas es incorrecta? En realidad “ninguna”. La clase &lt;a href="http://java.net.uri/"&gt;java.net.Uri&lt;/a&gt; implementa correctamente la resolución de caminos relativos según la&amp;nbsp;&lt;a href="http://www.faqs.org/rfcs/rfc2396.html"&gt;RFC2396&lt;/a&gt;, de agosto de 1998, y por ello elimina el “loquesea.do” antes de añadir el camino relativo. En cambio&amp;nbsp;&lt;a href="http://www.w3.org/TR/html401/struct/links.html#h-12.4.1"&gt;HTML 4.1&amp;nbsp;está basado en la especificación RFC1808&lt;/a&gt;, de junio de 1995, la cual dice que si hay “query string” se mantiene la dirección completa como base. Lo curioso es que HTML 4.1 es de diciembre de 1999, más de un año después de que la&amp;nbsp;&lt;a href="http://www.faqs.org/rfcs/rfc1808.html"&gt;RFC1808&lt;/a&gt;&amp;nbsp;fuera “sobre-escrita” por la &lt;a href="http://www.faqs.org/rfcs/rfc2396.html"&gt;RFC2396&lt;/a&gt;&amp;nbsp;pero sin embargo, parece al escribir la especificación de HTML no se fijaron en que la &lt;a href="http://www.faqs.org/rfcs/rfc1808.html"&gt;RFC1808&lt;/a&gt; ya no estaba en vigor. De todas formas, el comportamiento de los navegadores es correcto ya que &lt;a href="http://www.w3.org/TR/html401/struct/links.html#h-12.4.1"&gt;HTML 4.1 se basa en la RFC1808&lt;/a&gt; y ésta es la que hay que seguir. Y la clase java.net.Uri tampoco hace nada “incorrecto” ya que en realidad sigue una especificación más reciente. En realidad podría hacerlo “mejor” si permitiera especificar si el método resolve() ha de funcionar según &lt;a href="http://www.w3.org/TR/html401/"&gt;HTML 4.1&lt;/a&gt; o el futuro HTML 5, y puestos a tener un comportamiento por defecto...&amp;nbsp;&lt;a href="http://www.w3.org/TR/html401/"&gt;HTML 4.1&lt;/a&gt;&amp;nbsp;es muy común.... en fin, que la cosa es bastante ambigua así que la mejor solución es no escribir nunca enlaces de esa forma y escribirlos al menos un poco más explícitos, o si hay que tratar en Java las páginas HTML que escribe un tercero... si esas páginas son&amp;nbsp;&lt;a href="http://www.w3.org/TR/html401/"&gt;HTML 4.1&lt;/a&gt;&amp;nbsp;hay que tener en cuenta que java.net.Uri no resuelve las direcciones relativas como lo hacen los navegadores, así que hay que tratar la dirección antes de pasársela para obtener el resultado esperado.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Y ahí queda escrito ese aviso para navegantes, por si a alguien le ahorra un buen rato de investigación leyendo especificaciones y el código del &lt;a href="http://openjdk.java.net/"&gt;OpenJDK&lt;/a&gt; como he tenido que hacer yo.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-5540947640092441919?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/5540947640092441919/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/09/paths-relativos-especificaciones-java-y.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/5540947640092441919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/5540947640092441919'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/09/paths-relativos-especificaciones-java-y.html' title='Paths relativos, especificaciones, Java y un bug en &quot;Internetes&quot;'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-6160487930483908102</id><published>2010-09-20T21:38:00.000+02:00</published><updated>2010-09-20T21:38:24.937+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='profesion'/><title type='text'>Dime tu lista de prioridades y te diré lo profesional que me pareces</title><content type='html'>&lt;div style="margin-bottom: 0cm;"&gt;Un tema interesante y que da para muchas discusiones es “¿qué convierte/define a un buen informático?” Sin ánimo de sentar cátedra ni de que los demás piensen igual, voy a dar mi opinión personal e intrasferible de lo que define a un profesional del desarrollo de software (no a cualquier tipo de informático) pero siendo un pelín original. En vez de decir la típica lista de atributos, simplemente me fijaré en un aspecto que es: El orden de prioridades a la hora de afrontar un trabajo.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Así que sin más dilaciones, la pregunta crítica: “¿A que le das más importancia a la hora de desarrollar un software?&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A aplicar los patrones X, Y y  Z&lt;/b&gt;. Si esa es tu prioridad más alta o está entre las más  altas, lo siento pero entras dentro de la categoría  empezar-la-casa-por-el-tejado. Los patrones son soluciones comunes y  conocidas para ciertos tipos de problemas, y si tu interés se  centra en usarlos sin saber si son aplicables a tu problema,  ciertamente tienes el orden de las prioridades un poco confundidas.  El desarrollo no es un concurso para aplicar el mayor número de  patrones posibles, ni aplicar patrones significa garantía de nada  (de ahí el invento de los anti-patrones) así que la hora de pensar  en aplicar patrones está bastante más abajo, una vez sepamos a qué  problemas no estamos enfrentando y en caso de reconocer algún  problema habitual que se solucionar con un patrón, nada mejor que  aplicarlo. Pero no antes.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A aplicar las más modernas y  “más mejores” técnicas de desarrollo&lt;/b&gt;. Si este objetivo lo  tienes muy alto significa que te dejas llevar por las modas y que  eres una “fashion-victim”, de las que se tragan eso de que todo  lo nuevo es mejor y que lo viejo huele a rancio. Como pez en el agua  en el mercado consumista, pero en versión tecnológica. Si es solo  producto de la juventud y no de la falta de neuronas, no te  preocupes que se cura con la experiencia :). Cuando veas a la misma  gente contando cada X tiempo mentiras nuevas, por que con las viejas  no se hace negocio, aprenderás a ver detrás del humo y distinguir  lo aprovechable de las novedades y lo que hay que conservar de lo  que ya existe, siempre en función del tipo de trabajo que tengas.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A que el diseño sea orientado  a objetos (sustituible por cualquier concepto purista)&lt;/b&gt;. Este  tipo de objetivos están muy bien para el mundo teórico, pero  tienen ese ligero problemilla de que a la tozuda realidad a veces no  le gusta jugar con reglas perfectas. Seguro que eres de los que  creen que la tierra es perfectamente redonda y el mar es azul, al  fin y al cabo los pintan así en todos los libros ¿verdad? Pues no,  la perfección teórica está muy bien pero &lt;u&gt;sirve a un fin&lt;/u&gt;&lt;span style="text-decoration: none;"&gt;,  no es la finalidad en sí misma. Es decir, que el diseño sea lo más  orientado a objetos posible persigue un fin, que es obtener los  beneficios de la OO etc. pero si no vamos a conseguir esos  beneficios, por diversas razones, entonces ya no tiene utilidad.&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A usar el lenguaje/framework  X&lt;/b&gt;.Seamos sinceros, la mayoría de los proyectos sabemos en que  lenguaje/arquitectura los vamos a realizar por que es en el que  realizamos casi todos, si no todos, nuestros proyectos y no vamos a  re-evaluar nuestra cartera de soluciones en cada proyecto (en la  mayoría de casos, en algunas empresas lo hacen pero por necesidad).  Aun así, a la hora de cambiar de lenguaje/arquitectura en un  proyecto hay que recordar qué es lo realmente importante, y no, no  es usar una solución específica a no ser que el proyecto tenga ese  objetivo concreto.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A cumplir lo que se me mande  hacer, que para eso me pagan&lt;/b&gt;. Esta actitud conformista y muy  habitual no es que sea terriblemente mala, pero no le convierte a  uno en un gran profesional. Especialmente en nuestro campo donde  muchos mandos intermedios no saben lo que significan la mitad de las  siglas que usan y se limitan a distribuir la mierda que cae desde  arriba entre los de abajo. Repito, es algo comprensible pero por  hacer un examen justito a uno no le ponen un 10.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A disfrutar trabajando&lt;/b&gt;. Sí,  numerosos estudios demuestran que la gente trabaja mejor cuando está  más contenta y en trabajos creativos como el nuestro es muy  importante la mentalidad y la actitud, pero hombre, “un hombre ha  de hacer lo que tiene que hacer” y la vida no es una fiesta  constante. Si toca hacer un mantenimiento, se hace, aunque hayamos  hecho mil. Es importante que el trabajo sea gratificante, pero  tambien hay que tratar de disfrutar con nuestro trabajo. Como dicen  los sabios “no es más feliz quien más tiene, si no quien menos  desea”. O dicho de otro modo: “Señor, dame fuerza para aceptar  las cosas que no puedo cambiar, valor para cambiar las cosas que  puedo y sabiduría para poder diferenciar entre unas y otras.”&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A hacer lo que el cliente pide&lt;/b&gt;.  Esta es otra actitud comprensible, decente... pero estamos hablando  de informática, no de una tienda de ropa, y aquí el cliente no  tiene siempre la razón, simplemente por que el experto en manejo de  la información se supone que eres tú, y no él. Hacer lo que el  cliente dice sirve para cubrir el expediente y salvaguardar el culo,  pero no se lleva el máximo galardón en mi lista.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;A solucionar “El Problema”&lt;/b&gt;.  Obviamente, como último en la lista, esta es la que yo considero  que es la más profesional de las prioridades. Hay que entender cual  es “El Problema” que queremos solucionar, ayudados por el  cliente, usando el lenguaje y framework adecuados, que normalmente  será los que conocemos ya que que los conozca el equipo de  desarrollo da muchos puntos, utilizado las técnicas y patrones  adecuados y si encima podemos disfrutar haciéndolo, mejor que  mejor. Con esto lo que quiero decir es que la prioridad es dar  respuesta al problema. Los programas, de momento, no son obras de  arte para ser admiradas sin más, tienen una finalidad y cumplirla  debería ser la máxima prioridad, la cual muchas veces se combina  con el resto ya que ayudan a cumplirla, pero siempre hay que tener  el orden claro.&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="margin-bottom: 0cm;"&gt;Y esa es mi lista. Como se puede ver soy del modelo pragmático, pero es lo que hay. La lista no será del agrado de todos pero lo que está claro es que es la mía y sobre eso no hay discusión. ¿Y la tuya?&lt;/div&gt;&lt;br /&gt;Happy coding!&lt;br /&gt;EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-6160487930483908102?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/6160487930483908102/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/09/dime-tu-lista-de-prioridades-y-te-dire.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6160487930483908102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6160487930483908102'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/09/dime-tu-lista-de-prioridades-y-te-dire.html' title='Dime tu lista de prioridades y te diré lo profesional que me pareces'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-1478081487822837724</id><published>2010-09-02T21:40:00.001+02:00</published><updated>2010-09-02T21:44:30.133+02:00</updated><title type='text'>Arquitectura ligera y uso de cachés</title><content type='html'>&lt;div style="margin-bottom: 0cm;"&gt;Después de &lt;a href="http://lospuntossobrelasjotas.blogspot.com/2010/08/ejemplo-de-arquitectura-ligera.html"&gt;explicar un poco la arquitectura ligera que estoy empleando en un proyecto&lt;/a&gt; y dado que estoy usando &lt;a href="http://www.opensymphony.com/oscache/"&gt;una caché&lt;/a&gt;, tema sobre el cual ya &lt;a href="http://lospuntossobrelasjotas.blogspot.com/2010/08/reflexiones-sobre-el-uso-de-caches-i.html"&gt;desvarié un poco&lt;/a&gt;, he pensado que también podría ser interesante explicar cómo estoy usando la caché en este proyecto, basándome en las reflexiones del &lt;a href="http://lospuntossobrelasjotas.blogspot.com/2010/08/reflexiones-sobre-el-uso-de-caches-i.html"&gt;mensaje sobre uso de cachés&lt;/a&gt;.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Como ya expliqué, el proyecto consiste en mostrar los resultados de las partidas de un juego online, quien participó, cómo lo hizo cada piloto y estadísticas recopilatorias sobre el historial de cada piloto. Respondamos entonces a las preguntas clave:&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;¿Merecía la pena introducir una caché?&lt;/b&gt; La respuesta en este caso es que claramente sí, dado que vamos a mostrar resultados acumulados de varias tablas, especialmente en el caso de las estadísticas históricas. Incluso la “simple” lista de partidas jugadas es una consulta que cruza 4 tablas ( no por que esté mal el esquema de BDD si no por que ya muestra datos acumulados) y las primeras pruebas ya indican un incremento en el rendimiento de x30 en las consultas más simples.  &lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;¿En que capa he introducido la caché?&lt;/b&gt; Dejar que la BDD se encargue de ello implicaría que todavía tendríamos que viajar por la red hasta la BDD. La cache de &lt;a href="http://www.mybatis.org/"&gt;MyBatis &lt;/a&gt;no es algo con lo que he experimentado y no se que control me da (creo que no suficiente por lo que he leído en las listas de distribución), así que seguimos subiendo. El control de caché del framework ligero lo conozco bien y se que control me da: más que suficiente. Podría intentar subir aun más y usar la caché a nivel de filtro de servlets en la salida final (HTML o JSON), pero dado que más tarde introduciremos personalizaciones para que los usuarios vean sus propias páginas y eso implica que puede que la salida final de cada usuario sea ligeramente diferente, me quedo justo en el nivel de abajo. De todas formas, el salto gordo en el rendimiento se produce en este caso, como en muchos, en la consulta a la BDD y la creación de los objetos correspondientes en el servidor, así que haciendo caché de eso ya obtengo un gran beneficio.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;¿Como vamos a mantener actualizada la caché?&lt;/b&gt; En la aplicación hay 3 tipos de páginas, clasificándolas en relación de dependencia con datos actualizables:&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0cm;"&gt;Grupo 1: Las que dependen de todas  las partidas jugadas: Lista de partidas, estadísticas globales,  clasificaciones de todos los jugadores, etc.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0cm;"&gt;Grupo 2: Las que dependen de un  piloto en particular: Estadísticas generales de cada piloto,  detalles sobre los elementos utilizados por el piloto en sus  partidas...&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0cm;"&gt;Grupo 3: Las que no dependen de  nada: Datos de una partida y detalles de participación de cada  piloto en esa partida.&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="margin-bottom: 0cm;"&gt;Una vez sabemos esto, podemos ver que cada vez que haya una o más partidas nuevas, tenemos que invalidar todos los elementos de la caché del grupo 1 e invalidar los elementos del grupo 2 de los pilotos que hayan participado en las partidas nuevas. Los elementos del grupo 3 pueden estar en caché “indefinidamente”. Para llevar a cabo esta tarea lo que hemos hecho es implementar una tarea periódica con &lt;a href="http://www.quartz-scheduler.org/"&gt;Quartz &lt;/a&gt;que a intervalos regulares comprueba si ha habido partidas nuevas y en caso afirmativo, procede a marcar como caducados los elementos de la caché según las reglas definidas.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;¿Cual es el perfil de nuestra aplicación?&lt;/b&gt;&lt;span style="font-weight: normal;"&gt; Se presupone una aplicación donde habrá muchas más lecturas que actualizaciones, y donde las actualizaciones de datos no son críticas en absoluto. Por tanto el tipo de funcionamiento definido cuadra perfectamente.&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;b&gt;Puntos extra&lt;/b&gt;&lt;span style="font-weight: normal;"&gt;: Para controlar mejor el uso que se hace de la caché y verificar que todo funciona correctamente, tenemos varias ayudas:&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;span style="font-weight: normal;"&gt;Por  un lado, &lt;/span&gt;&lt;b&gt;el framework que utilizamos de caché nos puede decir en  cada momento el grado de utilización que le estamos dando&lt;/b&gt;&lt;span style="font-weight: normal;"&gt; (Hit/miss  ratio) por lo que podemos saber si realmente se está leyendo mucho  más de lo que se está actualizando la cache.&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;span style="font-weight: normal;"&gt;Por  otro lado, &lt;/span&gt;&lt;b&gt;podemos controlar la periodicidad de la tarea de control  de la cache&lt;/b&gt;&lt;span style="font-weight: normal;"&gt; y si vemos que la el grado de utilización es bajo y la  caché no se usa lo suficiente, podemos aumentar el período y así  aumentar el grado de utilización, &lt;/span&gt;&lt;b&gt;protegiendo &lt;/b&gt;a la vez&lt;b&gt; la BDD&lt;/b&gt;&lt;span style="font-weight: normal;"&gt; de  una carga excesiva.&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;span style="font-weight: normal;"&gt;Ambos  elementos &lt;/span&gt;&lt;b&gt;se pueden retocar en tiempo de ejecución&lt;/b&gt;&lt;span style="font-weight: normal;"&gt; sin necesidad de  reiniciar la aplicación, lo cual nos permite una gestión muy  cómoda sin miedo a molestar a los usuarios.&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="margin-bottom: 0cm;"&gt;Espero que este ejemplo sirva para ilustrar lo que quería decir en el mensaje de &lt;a href="http://lospuntossobrelasjotas.blogspot.com/2010/08/reflexiones-sobre-el-uso-de-caches-i.html"&gt;reflexiones sobre el uso de cachés&lt;/a&gt;, y si os da algunas ideas para vuestras aplicaciones, mejor que mejor.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Happy coding! EJ&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-1478081487822837724?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/1478081487822837724/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/09/arquitectura-ligera-y-uso-de-caches.html#comment-form' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/1478081487822837724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/1478081487822837724'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/09/arquitectura-ligera-y-uso-de-caches.html' title='Arquitectura ligera y uso de cachés'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-2671304737249314444</id><published>2010-08-28T14:49:00.004+02:00</published><updated>2010-08-30T11:22:04.410+02:00</updated><title type='text'>Ejemplo de arquitectura ligera</title><content type='html'>Una entrada corta sobre una arquitectura que estoy usando ahora mismo para una aplicación web de tamaño pequeño/mediano. La historia es que un grupo de "modders" estamos haciendo unas modificaciones a un juego multi-jugador online y una de las partes modificadas incluye recoger estadísticas de las partidas que se juegan: información del servidor y mapa que se jugó, qué equipo ganó, cuantas veces mataron y mató cada jugador, cuantos puntos... Y esta información la queremos publicar por web, que es la parte que me toca a mí (mi vena artística no da más que hacer cuatro palotes y mi C está demasiado oxidado como para meter mano al juego).&lt;br /&gt;&lt;br /&gt;Así que aquí está lo que uso, por si a alguien le da alguna idea:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Como framework-pegamento, uso uno "custom" que es simple y ligero, no necesito más, y por ahí no pienso recomendar nada: es mal negocio :).    &lt;/li&gt;&lt;li&gt;La interfaz la estoy haciendo principalmente con tablas y elementos de &lt;a href="http://developer.yahoo.com/yui/2/"&gt;YUI&lt;/a&gt;, usando AJAX y JSON para &lt;a href="http://developer.yahoo.com/yui/paginator/"&gt;paginar&lt;/a&gt; las &lt;a href="http://developer.yahoo.com/yui/datatable/"&gt;tablas&lt;/a&gt; sin recargar toda la página.    &lt;/li&gt;&lt;li&gt;Para formatear el JSON y las páginas que hacen de contenedores de los elementos &lt;a href="http://developer.yahoo.com/yui/2/"&gt;YUI&lt;/a&gt;, &lt;a href="http://java.sun.com/products/jsp/"&gt;JSP&lt;/a&gt; me basta. Suelo usar otras tecnologías para esto, pero como otros miembros del equipo puede que me ayuden y &lt;a href="http://java.sun.com/products/jsp/"&gt;JSP&lt;/a&gt; es lo más común y sencillo...    &lt;/li&gt;&lt;li&gt;Para hacer más "agradables" las URL y más intuitivas, fácil de recordar y de escribir, pero sin que me condicionen la implementación por debajo:&amp;nbsp;&lt;a href="http://tuckey.org/urlrewrite/"&gt;Url Rewrite Filter&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Para "decorar" las páginas y hacer que los estilos y menús sean comunes: &lt;a href="http://www.opensymphony.com/sitemesh/"&gt;SiteMesh&lt;/a&gt;.    &lt;/li&gt;&lt;li&gt;Para evitar machacar la BDD con consultas cuando los datos no han cambiado, se usa una cache  implementada con &lt;a href="http://www.opensymphony.com/oscache/"&gt;OSCache&lt;/a&gt;.    &lt;/li&gt;&lt;li&gt;Para las tareas que comprobarán periódicamente cuando hay que marcar como caducados algunos elementos de la caché (al acabar una partida, por ejemplo, hay que "caducar" todas las estadísticas globales de los jugadores que participaron y la lista general de partidas jugadas) uso&amp;nbsp;&lt;a href="http://www.quartz-scheduler.org/"&gt;Quartz&lt;/a&gt;.    &lt;/li&gt;&lt;li&gt;Para consultar la BDD, como los datos que se muestran son principalmente recopilatorios, las consultas suelen implicar media docena de "joins" y la navegación por entidades mataría el rendimiento, uso &lt;a href="http://www.mybatis.org/"&gt;MyBatis&lt;/a&gt; en lugar de &lt;a href="http://es.wikipedia.org/wiki/Java_Persistence_API"&gt;JPA&lt;/a&gt;, que es lo que seguramente use para atacar la parte de autenticación, definición de equipos etc. que sigue otra estructura más orientada a objetos.    &lt;/li&gt;&lt;li&gt;Para no tener que escribir tropecientos getter y setter y dejar el código limpio, uso &lt;a href="http://projectlombok.org/"&gt;Lombok&lt;/a&gt; para que lo haga por mí sin siquiera tener que verlos en el código. Muy útil en este caso.    &lt;/li&gt;&lt;li&gt;La BDD es &lt;a href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt;. No la escogí yo ni hice el esquema así que poco puedo decir excepto que funciona.    &lt;/li&gt;&lt;li&gt;Para crear, probar  y depurar las consultas contra la BDD: &lt;a href="http://www.aquafold.com/"&gt;Aqua Data Studio&lt;/a&gt;.    &lt;/li&gt;&lt;li&gt;Para no tener que reiniciar el contexto cada vez que hago un cambio en las clases Java: &lt;a href="http://www.zeroturnaround.com/jrebel/"&gt;JRebel&lt;/a&gt;, aunque tiene algunos conflictos con &lt;a href="http://projectlombok.org/"&gt;Lombok&lt;/a&gt;. Por otro lado, el framework detecta los cambios en la configuración de &lt;a href="http://www.mybatis.org/"&gt;MyBatis&lt;/a&gt; en ejecución y recarga los "Mapper", así que tengo que re-iniciar el contexto muy muy poco. Y para rizar el rizo, el tiempo de reinicio con este framework ligero y &lt;a href="http://www.mybatis.org/"&gt;MyBatis&lt;/a&gt; no llega a los 4 segundos así que el famoso tiempo perdido esperando a que se (re)inicien las aplicaciones Java en este caso es irrisorio.    &lt;/li&gt;&lt;li&gt;Como IDE: &lt;a href="http://www.eclipse.org/"&gt;Eclipse&lt;/a&gt;, aunque el proyecto se puede montar solito en base a &lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt; + &lt;a href="http://ant.apache.org/ivy/"&gt;Ivy&lt;/a&gt; e incluso ejecutarlo, ya que incluye contenedor de servlets embebido para pruebas. No tengo manías y casi cualquier IDE debería valer en este caso, pero uso el que me es más cómodo.&lt;/li&gt;&lt;li&gt;Para depurar YUI: El &lt;a href="http://www.mozilla-europe.org/es/firefox/"&gt;Firefox&lt;/a&gt; con &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt;, como no, aunque a veces interfiere con otros plugin y últimamente me tiene algo mosqueado.&lt;/li&gt;&lt;li&gt;Para comprobar que el JSON que envío es correcto cuando me da un problema y no se si es por la estructura de JSON o un fallo en JavaScript: &lt;a href="http://www.jsonlint.com/"&gt;JSONLint&lt;/a&gt; &lt;/li&gt;&lt;li&gt;Como contenedor de servlets para pruebas: &lt;a href="http://www.mortbay.org/"&gt;Jetty 6,&lt;/a&gt; &lt;a href="http://tomcat.apache.org/"&gt;Tomcat 5.5&lt;/a&gt; y &lt;a href="http://www.caucho.com/"&gt;Resin 4&lt;/a&gt;, en sus formatos "embedido" respectivos y lanzados desde el&amp;nbsp;&lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt;&amp;nbsp;o a mano.&amp;nbsp;El de producción, que tampoco escojo yo, será &lt;a href="http://tomcat.apache.org/"&gt;Tomcat 5.5&lt;/a&gt;, así que estoy cubierto. &lt;/li&gt;&lt;/ul&gt;De momento las pruebas iniciales son bastante satisfactorias y gracias al uso de paginación a través de AJAX y de cachés en el servidor, parece que aguantaremos la carga. Para este sistema en particular es más importante que la aplicación sea ligera y ágil, que usar grandes frameworks que nos den muchas cosas hechas que no vamos a usar o que nos permitan hacer virguerías que no haremos.&lt;br /&gt;&lt;br /&gt;Cada aplicación tiene sus propias cosas e intentar aplicar siempre la misma receta es como intentar cocinar el pollo, el cordero y el cerdo de la misma forma. Algunas veces puede funcionar pero otras es un desastre incomestible. Así que como en las recetas de cocina, si alguien puede aprovechar partes y le sirven para sus circunstancias particulares, me alegro. Si no, pues mala suerte y a buscar sus propias soluciones :).&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Bon Appétit &amp;amp; Happy coding!&lt;br /&gt;EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-2671304737249314444?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/2671304737249314444/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/ejemplo-de-arquitectura-ligera.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2671304737249314444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2671304737249314444'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/ejemplo-de-arquitectura-ligera.html' title='Ejemplo de arquitectura ligera'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-6391076762769803353</id><published>2010-08-23T22:28:00.001+02:00</published><updated>2010-08-24T23:54:18.599+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='opinion guru'/><title type='text'>Sobre Gurús y otros falsos mitos</title><content type='html'>Hoy un tema que seguramente levante algo de polvo, pero para eso estamos al fin y al cabo :).&lt;br /&gt;Lo primero de todo: debo admitir que el "fenómeno gurú" es algo que no va conmigo y que personalmente me produce vergüenza ajena. Lo de elevar a los altares a una persona para considerarla un "gurú" se comprende en quinceañer@s con más hormonas que cerebro, pero que una persona adulta tenga tan poco consideración por si mismo como para sublimar sus opiniones a otra, por el mero hecho de la fama que tiene... en fin, que allá cada cual pero a mí que no me busquen para eso.&lt;br /&gt;&lt;br /&gt;Volviendo al tema, una de anécdotas:&lt;br /&gt;Eranse cuatro desarrolladores sentados a una mesa disfrutando de una cena y unas bebidas, dos de los cuales eran gurús de los gordos, de los que su nombre está semana sí, semana no en java.net, Dzone etc. Los otros dos éramos dos "soldados de trinchera", de los que el pan se lo ganan dándole a la tecla, coordinando nuestros proyectos, con nuestras ponencias en congresos de vez en cuando, nuestras colaboraciones en proyectos OS, pero fama: ni de lejos, ni ganas.&lt;br /&gt;Una cena agradable en la que que apareció uno de los temas recurrentes: "para desarrollar me basta con el vi". Los dos gurús argumentando que los hombres de pelo en pecho de verdad sólo necesitan el vi, nosotros argumentando que saber hacerlo a pelo es imprescindible pero que los editores modernos con sus capacidades de refactoring, búsquedas de referencias etc. son una gran ayuda. Ellos que un buen desarrollador tiene todo el proyecto en su cabeza y que igualmente, hackeando el vi y con unas extensiones de no-se-donde hacía lo mismito, y total, ¿Quién necesita refactorizar si basta con escribir todo el código bien de un tirón y a la primera? Mi compañero de trinchera decía que en sus proyectos, donde a veces trabajan hasta seiscientos (sí, tantos) programadores en un sólo proyecto, pues tener formateadores estandarizados, el FindBug, y las capacidades de "refactoring", repositorios de versiones y dependencias integradas etc. sirven para que todo el mundo esté a un nivel parecido y no sólo sirvan los mega-cracks, aparte de hacer que los mega-cracks sean aun más eficientes.&lt;br /&gt;Al final, como en todos estos temas que no tienen demostración matemática, "ni pa' ti, ni pa' mí" si no cada uno con su opinión. Se notó algo que estaban más acostumbrados a que la gente dijera "amén" a sus opiniones por ser quienes eran, pero tampoco tuvieron mucho problema al toparse con dos agnósticos de la fe gurú.&lt;br /&gt;Ahí hubiera quedado la cosa, si no fuera por que al día siguiente en el congreso en el que estábamos le tocaba dar una charla a uno de los dos gurús, el cual ciertamente de su tema sabe un huevo y la yema del otro y mucho más que un servidor. En su charla le tocaba una demostración en vivo de ciertas técnicas y cómo el código podía afectarles, así que tenía su ejemplo preparado que consistía en unas clases Java muy simples para compilar y ejecutar.&lt;br /&gt;La demo empieza bien, ejecuta el código, muestra los resultados que esperaba y todo perfecto.&lt;br /&gt;Entonces toca modificar ligeramente la clase Java para comprobar una de las técnicas. Abre el vi, realiza unas modificaciones, borrando líneas enteras de texto con la x, repitiendo el mismo comando varias veces re-haciéndolo entero, y a la hora de salir guarda con :w y luego sale con :q. Para los que no uséis vi y no lo hayáis pillado, así es como trabaja alguien que sólo conoce los comandos básicos.&lt;br /&gt;Pero bueno, guarda los cambios y compila con javac especificando todo a mano (¿Ant? Eso es para mariquitas). Resultado: No compila. En realidad hace falta modificar también otra clase donde se llama a la primera y como ha cambiado unos parámetros en un método... Eso lo puedo decir yo a 20m leyendo la linea que escupe javac, pero él parece no pillarlo (entiendo que los nervios en escena afectan). Al final alguien se lo chiva y abre el otro fichero, realiza otras modificaciones con la misma "habilidad" con el vi, guarda y sale. A compilar otra vez. Resultado: No compila. Esta vez es por que una de las letras llamando al procedimiento está mal, es mayúscula y ha de ser minúscula, y por eso no funciona. Etc. etc.&lt;br /&gt;&lt;br /&gt;Resumiendo: Tuvo que saltarse la demo por que cuando no le fallaba la compilación con el javac, poniendo todos los parámetros a mano, tenía un error tonto en el código y tenía que arreglarlo, lo cual le costaba tiempo por que hasta interpretar los mensajes del javac le costaba en la tensión del momento. Y una cosa que quedó clara como el agua es que no se dedica a ganarse la vida picando código, cosa que yo ya sabía.&lt;br /&gt;Conclusión: Cuando habla de su tema y está en su salsa, le escucho con los ojos abiertos. Cuando habla de otras cosas, como por ejemplo formas de picar código, valoro mucho más mi opinión o la del otro compinche de trincheras que la suya, con todo el respeto y sin desmerecer, que lo cortés no quita lo valiente.&lt;br /&gt;&lt;br /&gt;Otra anécdota aun más cortita: Estaba yo en un congreso importante y gracias a un contacto que tengo, importante en el mundillo que no famoso, acabo cenando con un grupo donde hay "un famoso" que ha escrito un libro sobre un tema nuevo de Java, el cual todo el mundo recomienda como la Biblia del saber. Al llegar a la cena el famoso resulta ser un chico joven de veinti-pocos con cara de niño (nada en contra de la juventud, pero uno se imagina a un gurú con una larga barba blanca y profundas arrugas de meditar a la intemperie :) ). Hasta aquí nada reseñable excepto la sorpresa de descubrir una cara tan joven asociada a una tan famosa "fuente de conocimiento". Lo mejor viene durante la cena cuando movido por la curiosidad le preguntó cómo acabó escribiendo el libro ese que le hizo tan famoso. La respuesta es lo que me deja tieso: Acababa de aprender Java y nunca había trabajado con el tema del libro, así que cuando un contacto le propuso escribir un libro sobre el tema, pensó: "así aprenderé de que va esto". O sea, sin quitar méritos al libro que fue una gran guiá de iniciación para muchos, cuando el tío lo empezó a escribir no tenía ni la más remota idea del tema, y no demasiada de Java. Y ésta es la persona cuyas opiniones sobre el tema la gente pone por encima de las demás... en fin serafín.&lt;br /&gt;&lt;br /&gt;Por mi parte, todos somos humanos y tenemos cosas de las que sabemos, cosas de las que no. Saber escribir un buen libro para iniciados no significa conocer un tema en profundidad, ser un experto en un tema concreto no nos convierte en expertos en todos los temas y dejarse llevar por la fama de la gente para poner sus opiniones de cualquier cosa por encima de otras, por el mero hecho de ser famosos, es una soplapollez que debería sonrojarnos. &lt;br /&gt;&lt;br /&gt;Así que si te encuentras con un experto en un tema y te da su opinión sobre ese tema, escúchale, si te la da sobre cualquier otra cosa, recuerda que a igualdad de "conocimientos" la suya es tan válida como la de cualquiera.&lt;br /&gt;&lt;br /&gt;Happy coding!&lt;br /&gt;EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-6391076762769803353?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/6391076762769803353/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/sobre-gurus-y-otros-falsos-mitos.html#comment-form' title='5 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6391076762769803353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6391076762769803353'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/sobre-gurus-y-otros-falsos-mitos.html' title='Sobre Gurús y otros falsos mitos'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-6534460178883401861</id><published>2010-08-21T01:14:00.000+02:00</published><updated>2010-08-21T01:14:47.191+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java enumeration'/><title type='text'>Uso avanzado de Enumeration</title><content type='html'>No parece que el tema de las cachés interese demasiado, así que mientras decido si continúo o no con el tema, otro consejo breve, en este caso sobre el uso de las &lt;a href="http://download.oracle.com/javase/1.5.0/docs/guide/language/enums.html"&gt;enumeraciones en Java&lt;/a&gt;.&lt;br /&gt;El uso básico que todo el mundo aprende rápidamente es el de simplemente hacer una lista de elementos para luego, por ejemplo, poderlos pasar como parámetros con verificación de tipos. Algunos desarrolladores, si embargo, se quedan ahí y piensan que su utilidad es limitada puesto que se puede sacar el valor con name(), se puede obtener el índice con ordinal() pero si necesitamos más, tenemos que complicar mucho la cosa... y total para pasar una cadena...&lt;br /&gt;Nada más lejos de la realidad, puesto que extender una enumeración para que nos sea más útil es tremendamente sencillo.&lt;br /&gt;Pongamos un caso parecido a uno con el que trabajé hace poco: Queremos mostrar una lista de empleados de forma ordenada, pudiendo pasar el criterio de ordenación como parámetro, o sea una cadena. Para ordenar una lista de elementos necesitamos un Comparator y no queremos ni que el parámetro a pasar sea un nombre muy largo ni que el nombre de la enumeración sea demasiado corto ni se aparte de las convenciones de código que usamos, así que... ¿como lo podemos hacer?&lt;br /&gt;Muy sencillo:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Añadimos dos campos a la enumeración: acrónimo y comparador y los inicializamos al crear cada elemento.&lt;/li&gt;&lt;li&gt;Dado que querremos obtener el elemento correcto a partir del acrónimo, no del nombre completo, creamos una tabla de búsquedas inversa, indexada por acrónimos. La creamos estática e inicializada al cargar la clase y siempre la tendremos disponible y preparada, para no tener que buscar en un bucle.&lt;/li&gt;&lt;/ul&gt;Y el código es igualmente sencillo:&lt;br /&gt;&lt;pre class="brush: java"&gt;  static Comparator ComparadorPorNombre = new Comparator()&lt;br /&gt;  {&lt;br /&gt;   //...&lt;br /&gt;  };&lt;br /&gt;  static Comparator ComparadorPorSueldo = new Comparator()&lt;br /&gt;  {&lt;br /&gt;   //...&lt;br /&gt;  };&lt;br /&gt;  static Comparator ComparadorPorCargo = new Comparator()&lt;br /&gt;  {&lt;br /&gt;   //...&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  public enum Ordenacion&lt;br /&gt;  {&lt;br /&gt;    ORDENAR_POR_NOMBRE(ComparadorPorNombre, "nombre")&lt;br /&gt;    ,ORDENAR_POR_SUELDO(ComparadorPorSueldo, "sueldo")&lt;br /&gt;    ,ORDENAR_POR_CARGO(ComparadorPorCargo, "cargo");&lt;br /&gt;    &lt;br /&gt;    private static Map&amp;lt;String, Ordenacion&gt; mapaAcronimoOrdenacion;&lt;br /&gt;&lt;br /&gt;    private final String acronimo;&lt;br /&gt;    private final Comparator comparador;&lt;br /&gt;&lt;br /&gt;    private Ordenacion(Comparator comparador, String acronimo)&lt;br /&gt;    {&lt;br /&gt;      this.comparador = comparador;&lt;br /&gt;      this.acronimo = acronimo;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Comparator getComparador()&lt;br /&gt;    {&lt;br /&gt;      return comparador;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String getAcronimo()&lt;br /&gt;    {&lt;br /&gt;      return acronimo;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Método para obtener una ordenación según el acrónimo que nos hayan pasado.&lt;br /&gt;    // Devuelve null si la ordenación no existe.&lt;br /&gt;    public static Ordenacion getPorAcronimo(String acronimo)&lt;br /&gt;    {&lt;br /&gt;      return mapaAcronimoOrdenacion.get(acronimo);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Inicializamos el mapa inverso por acronimo al cargar la clase, también&lt;br /&gt;    // podríamos hacerlo de forma "lazy" en el primer acceso, si quisiéramos.&lt;br /&gt;    static&lt;br /&gt;    {&lt;br /&gt;      mapaAcronimoOrdenacion = new HashMap();&lt;br /&gt;      for (Ordenacion rt : Ordenacion.values())&lt;br /&gt;      {&lt;br /&gt;        mapaAcronimoOrdenacion.put(rt.getAcronimo(), rt);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Un breve ejemplo de como sería el uso.&lt;br /&gt;  public static void main(String[] arg) throws Exception&lt;br /&gt;  {&lt;br /&gt;    //...&lt;br /&gt;    String param_acronimo = ""; // lo obtenemos de alguna forma&lt;br /&gt;    Ordenacion ord = Ordenacion.getPorAcronimo(param_acronimo);&lt;br /&gt;    Set listaOrdenada = new TreeSet(ord.getComparador());&lt;br /&gt;    //...&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;De esta forma, podemos añadir nuevas ordenaciones "tranquilamente" añadiendo elementos a la enumeración y podemos saber que en todos los métodos a los que le pasemos una Ordenacion como parámetro seguirán funcionando, y que el control de si existe o no una Ordenacion con ese acrónimo se produce en un punto único, lo cual facilita el mantenimiento.&lt;br /&gt;&lt;br /&gt;Lo que he mostrado con un acrónimo y un comparador se puede aplicar a cualquier cosa que se os ocurra: Enumeración con tipos de datos admitidos y expresiones regulares para comprobar si el parámetro es correcto traducciones de acrónimos/códigos numéricos a nombres completos para mostrar al usuario en listas cortas que no estén en BDD...&lt;br /&gt;&lt;br /&gt;La cuestión es que si tenéis una lista de elementos y estáis pasando int o String y luego haciendo if/else o switch en varias partes de vuestro código, re-pensadlo a ver si podéis usar una enumeración y hacer el código "type safe" con la traducción de input -&gt; valor correcto en un sólo punto.&lt;br /&gt;&lt;br /&gt;Espero que os sirva y...&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-6534460178883401861?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/6534460178883401861/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/uso-avanzado-de-enumeration.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6534460178883401861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6534460178883401861'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/uso-avanzado-de-enumeration.html' title='Uso avanzado de Enumeration'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-2822588506731895686</id><published>2010-08-20T10:35:00.007+02:00</published><updated>2010-08-20T10:41:09.690+02:00</updated><title type='text'>Reflexiones sobre el uso de cachés I</title><content type='html'>Hoy un par de reflexiones sobre un tema interesante y complejo, aunque menos de lo que parece a primera vista: &lt;b&gt;el uso de cachés&lt;/b&gt;* (&lt;i&gt;me disculpo por adelantado del uso incorrecto de memoria caché y sus derivados, pero todos nos entenderemos y no encuentro sinónimo breve y adecuado&lt;/i&gt;).&lt;br /&gt;La utilización de cachés es una técnica muy útil para mejorar el rendimiento de nuestras aplicaciones, aunque sufre del mismo tipo de problemas que otras técnicas avanzadas para mejorar el rendimiento, como la programación multi-hilo: Es tremendamente útil pero a la vez peligrosa si no se usa adecuadamente, por lo que mucha gente no se encuentra cómoda usándola y prefiere optar por la prudencia y abstenerse. Sin embargo, en el fondo no es tan complicada de utilizar si sabemos lo que estamos haciendo, así que intentaremos explicar aquí algunos conceptos para ver si ayudan a entenderla mejor.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Qué es y por qué&lt;/h2&gt;Lo primero es dejar claro qué es lo que hacemos y por qué lo hacemos: La idea básica es que para obtener el resultado que necesitamos, nuestros programas siguen una serie de pasos que hay que repetir cada vez que pedimos ese resultado. Usar una cache no es más que almacenar alguno de los resultados intermedios para no tener que volver a calcularlo, evitando tener que repetir los pasos que llevan a calcular el resultado intermedio. Así de simple. La razón de hacerlo es aun más sencilla: realizar los pasos cuesta recursos (principalmente tiempo) y si podemos evitar tener que hacerlos, evitamos consumir esos recursos. Esta perogrullada de explicación cobra sentido cuando debamos tomar decisiones sobre si vale la pena usar cachés, dónde, cómo...&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Como atacar el problema&lt;/h2&gt;Uno de los errores comunes a la hora de implementar técnicas de cachés es empezar pensando cómo vamos a implementar nuestra caché, que librerías utilizar etc. Error. La implementación es importante, pero hay otros factores tanto o más importantes sobre los que merece la pena decidir antes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;¿Merece la pena introducir una caché?&lt;/b&gt; Ésta es la primera pregunta que deberíamos hacernos y no es trivial. Volviendo al primer punto, queremos introducir el uso de cachés para obtener unos resultados (ahorrar tiempo, consumo de CPU etc.) y si no lo vamos a conseguir, no merece la pena hacerlo. Aunque no lo parezca, hay muchos tipos de aplicaciones donde el uso de cachés no produce suficientes beneficios para el coste invertido. Para contestar a esta pregunta, es importante estudiar las respuestas a las preguntas que la siguen, ya que nos darán muchas pistas.&lt;/li&gt;&lt;li&gt;&lt;b&gt;¿A que nivel vamos a introducir la caché?&lt;/b&gt; En el diseño típico de una aplicación hay varias capas que tendremos que recorrer hasta llegar el resultado que deseamos, y muchas veces el resultado va sufriendo transformaciones por el camino, así que decidir cuál es el resultado intermedio que vamos a cachear es también una de las decisiones importantes.&lt;br /&gt;Por ejemplo, en una típica aplicación web multi-capa, no es lo mismo guardar el resultado de la consulta a la BDD, que los objetos Java que hayamos construido a partir del resultado de la consulta, que el trozo de JSP (p.e.) donde los hayamos pintado, que toda la página HTML resultado de la petición.&lt;br /&gt;Hay que pensar que cuanto más cerca estemos del origen del resultado, más fácil será de reutilizar, ya que habrá sufrido menos transformaciones, pero menos estaremos ganando ya que nos ahorraremos menos pasos. Al mismo tiempo, cuanto más complejo sea el resultado que guardemos en caché, más nos ahorraremos pero mayor será la probabilidad de que no nos sirva la próxima vez que lo queramos usar. &lt;br /&gt;Por ejemplo, pintar la fecha/hora actual en la página resultado, o el nombre del usuario, o tener filtros sobre las búsquedas puede hacer que la página HML entera no la podamos reutilizar para otras peticiones de otros usuarios, pero si bajamos un nivel y guardamos la información antes de añadir esa información personalizada y aplicar el filtro, ahorraremos menos pasos pero podremos reutilizar mucho más ese resultado intermedio.&lt;br /&gt;&lt;li&gt;&lt;b&gt;¿Cómo vamos a mantener actualizada la caché?&lt;/b&gt; Esta es la tercera pregunta importante y también nos da una pista muy importante sobre si merece la pena o no introducir una caché. Si hay algo mucho peor que un programa con bajo rendimiento por que no usa cachés, es un programa que devuelve resultados obsoletos por que no actualiza su caché cuando toca. Para poder mantener actualizada adecuadamente nuestra caché necesitamos tener claras dos cosas:&lt;ul&gt;&lt;li&gt;Identificar claramente cada elemento que guardamos en la cache para saber cuando lo podemos usar en vez de calcular de  nuevo el resultado y cuando no.&lt;/li&gt;&lt;li&gt;Cuales son las circunstancias en las cuales el resultado intermedio que tenemos en cachés es inválido y cómo invalidarlo. Por ejemplo, si se actualizan datos en una de nuestras tablas... ¿que objetos de la caché se ven afectados y cómo podemos invalidarlos? ¿Lo haremos por tiempo, usaremos eventos, lo hará el usuario de forma manual?&lt;/li&gt;&lt;/ul&gt;No poder responder claramente a estas preguntas o tener una respuesta muy compleja son signos de que quizá usar una caché no sea tan buena idea. Por ejemplo, si actualizar los datos de la tabla X nos invalida toda la caché y esa tabla se actualiza muy a menudo... quizá no nos sirva de nada tener una caché.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;¿Cual es el perfil de nuestra aplicación?&lt;/b&gt; Una vez visto lo anterior, hay que añadir que no es lo mismo una aplicación donde los datos cambian solamente una vez al año pero el cambio ha de ser visible inmediatamente, que una aplicación donde los datos se actualizan continuamente pero únicamente mostramos resultados consolidados que no cambian. No es lo mismo una aplicación donde el 90% de accesos son a un par de páginas que han de estar siempre actualizadas, que otra donde los accesos están repartidos semi-aleatoriamente entre miles de páginas, aunque cambien poco.&lt;br /&gt;Siguiendo con los ejemplos, una aplicación como la última mencionada donde se accede aleatoriamente a miles de páginas... o tenemos una caché donde poder tener las miles de páginas pre-calculadas o al no poder guardarlas todas y ser el acceso aleatorio, el uso de la caché puede ser muy bajo así que o nos caben todas en caché o quizá no merezca la pena. En cambio, en la anterior donde un 90% de acceso se producen a un par de páginas que han de estar siempre actualizadas, si montamos un sistema de eventos para tener la caché siempre actualizada podemos sacarle un gran partido a la caché, pero si el sistema para mantenerla actualizada es demasiado complejo e inestable quizá debamos descartar el uso de una caché a ese nivel.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;Así pues, el primer paso es responder a estas preguntas y decidir si merece la pena o no implementar una caché. Obviamente habrá muchos casos donde la respuesta no se decantara claramente en un sentido y podemos pasar a hacer pruebas y decidir con datos en la mano, pero como mínimo nos habrá servidor para tener claro cómo queremos hacerlo y qué cosas queremos comprobar en nuestra fase de evaluación.&lt;br /&gt;&lt;br /&gt;Hay mucho más que hablar sobre el tema, aunque espero que con esto os de alguna idea de como empezar a estudiar el tema. No quiero hacer este tocho más extenso de lo que es y lo dejaré aquí de momento. Si hay interés, continuaré con el tema explicando algunos perfiles típicos de aplicaciones y las soluciones técnicas más habituales.&lt;br /&gt;&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-2822588506731895686?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/2822588506731895686/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/reflexiones-sobre-el-uso-de-caches-i.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2822588506731895686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2822588506731895686'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/reflexiones-sobre-el-uso-de-caches-i.html' title='Reflexiones sobre el uso de cachés I'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-3751625527152152588</id><published>2010-08-18T17:12:00.001+02:00</published><updated>2010-08-18T17:39:08.320+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java serializable TreeSet'/><title type='text'>TreeSet y java.io.NotSerializableException</title><content type='html'>Después de un periodo sabático, un truco para solucionar un problemilla habitual por culpa de como está montado el JDK, sin ser realmente culpa de nadie... El problema en sí aparece al tratar de serializar un &lt;i&gt;TreeSet&lt;/i&gt;, o cualquier otra colección ordenada, en el cual hemos especificado un &lt;i&gt;Comparator&lt;/i&gt; creado como instancia anónima. En código:&lt;br /&gt;&lt;pre class="brush: java"&gt;static Comparator miComparador =&lt;br /&gt;    new Comparator()&lt;br /&gt;    {&lt;br /&gt;      @Override&lt;br /&gt;      public int compare(Object arg0, Object arg1)&lt;br /&gt;      {&lt;br /&gt;        //...&lt;br /&gt;        return valorAdecuado;&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;  public static void main(String[] arg)&lt;br /&gt;    throws Exception&lt;br /&gt;    {&lt;br /&gt;      Set miSetOrdenado = new TreeSet(miComparador);&lt;br /&gt;      // ... rellenamos el Set&lt;br /&gt;      ByteArrayOutputStream theBOS = new ByteArrayOutputStream();&lt;br /&gt;      ObjectOutputStream theOOS = new ObjectOutputStream(theBOS);&lt;br /&gt;      theOOS.writeObject(miSetOrdenado);&lt;br /&gt;      theOOS.close();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;Si intentamos ejecutar este código, nos saltará una excepción:&lt;br /&gt;&lt;i&gt;java.io.NotSerializableException: test.App$1&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Después de volverse tonto mirando por que el los elementos que uno pone en el &lt;i&gt;TreeSet&lt;/i&gt; no son serializables, para normalmente descubrir que sí lo son, al final resulta que el problema es el &lt;i&gt;Comparator&lt;/i&gt;. Entonces buscamos por Internet y las recomendaciones más habituales son hacer el campo &lt;i&gt;static&lt;/i&gt; o &lt;i&gt;transient&lt;/i&gt;, implementar &lt;i&gt;Serializable&lt;/i&gt;... pero resulta que el &lt;i&gt;Comparator&lt;/i&gt; lo tenemos declarado como &lt;i&gt;static&lt;/i&gt;, por lo tanto no debería afectar puesto que la clase App sería Serializable... ¿o no? La cuestión sin embargo es... ¿cual es realmente la instancia que no es Serializable?&lt;br /&gt;&lt;br /&gt;Para hacer la historia breve, el problema es que la instancia no Serializable es el TreeSet y que el campo que habría que marcar como &lt;i&gt;transient&lt;/i&gt; es donde se guarda el &lt;i&gt;Comparator&lt;/i&gt; de la clase &lt;i&gt;TreeSet&lt;/i&gt;, cosa imposible, así que sólo nos queda declarar que el &lt;i&gt;Comparator&lt;/i&gt; implementa &lt;i&gt;Serializable&lt;/i&gt;, pero... ¿Cómo lo hacemos si es una instancia anónima? La solución pasa por no usar una instancia de clase anónima y hacer una clase interna, la cual sí podemos declarar que es &lt;i&gt;Serializable&lt;/i&gt; y luego crear una instancia. Modificando ligeramente el código:&lt;br /&gt;&lt;pre class="brush: java"&gt;static class MiClaseComparadora&lt;br /&gt;    implements Comparator, Serializable&lt;br /&gt;  {&lt;br /&gt;    @Override&lt;br /&gt;    public int compare(Object arg0, Object arg1)&lt;br /&gt;    {&lt;br /&gt;      //...&lt;br /&gt;      return valorAdecuado;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  static Comparator miComparador = new MiClaseComparadora();&lt;br /&gt;&lt;br /&gt;  public static void main(String[] arg)&lt;br /&gt;    throws Exception&lt;br /&gt;    {&lt;br /&gt;      Set miSetOrdenado = new TreeSet(miComparador);&lt;br /&gt;      // ... rellenamos el Set&lt;br /&gt;      ByteArrayOutputStream theBOS = new ByteArrayOutputStream();&lt;br /&gt;      ObjectOutputStream theOOS = new ObjectOutputStream(theBOS);&lt;br /&gt;      theOOS.writeObject(miSetOrdenado);&lt;br /&gt;      theOOS.close();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;Y listo. Un detalle tonto que puede impedir que podamos serializar algunos objetos necesarios para nuestra aplicación y cuyo diagnóstico no es sencillo.&lt;br /&gt;&lt;br /&gt;En mi caso, necesitaba almacenar en OSCache una lista ordenada de elementos para no tener que re-ordenarlos cada vez y al pasar la cache de memoria a disco... zas!&lt;br /&gt;&lt;br /&gt;Espero que esto le evite a alguien tener que dar tantas vueltas por Internet como a mí, para acabar sin encontrar una solucón clara y concisa.&lt;br /&gt;&lt;br /&gt;Happy coding!&lt;br /&gt;EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-3751625527152152588?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/3751625527152152588/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/treeset-y-javaionotserializableexceptio.html#comment-form' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/3751625527152152588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/3751625527152152588'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/08/treeset-y-javaionotserializableexceptio.html' title='TreeSet y java.io.NotSerializableException'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-2296118296776633090</id><published>2010-06-22T19:34:00.002+02:00</published><updated>2010-06-22T19:37:12.316+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven java'/><title type='text'>Maven: como hacer fácil lo difícil y difícil lo fácil</title><content type='html'>&lt;b&gt;//RANT ON&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Debo confesar que mi primera idea para el titulo, llevado por la frustración, era "&lt;i&gt;Mierda de Maven&lt;/i&gt;", y eso era lo más fino que se me ocurría, pero teniendo en cuenta que por mi parte odio los titulares amarillistas en los posts de los blogs, he decidido ser coherente y poner algo más neutro, o casi :).&lt;br /&gt;&lt;br /&gt;La cuestión es que llevo unos días intentando transformar un proyecto de infraestructura desde Ant a Maven. Lo reconozco, nunca he sido fan de Maven pero resulta que quiero poder distribuir este proyecto en repositorios Maven y los buenos chicos de &lt;a href="http://www.sonatype.com/"&gt;SonaType&lt;/a&gt; ofrecen hospedaje gratutito de repositorio a los proyectos open source. Eso sí, usando Maven para crear y subir las "releases" etc.&lt;br /&gt;&lt;br /&gt;Así que después de mucho resistirme, me dije "¡amos a probar, hombre!" y como dicen los anglos decidí "morder la bala" (bite the bullet) o agarrarme los machos, como más os guste. Por el camino tuve que abandonar algunas cosas, como los productos del proyecto que no son .jar (tengo/tenía un .zip y un .xpi), y plegarme a las convenciones que los creadores de Maven consideran que son las buenas. Todo sea por la causa. &lt;br /&gt;&lt;br /&gt;Pero bueno, después de unas cuantas peleas, unos cuantos insultos al aire y mucho Google + prueba y error, consigo tener el proyecto montado más o menos como quiero. Los otros artefactos los hago con Ant y al final dejo un proyecto padre y un subproyecto. Sigo las instrucciones para desplegar los artefactos en el repositorio de Sonatype (ver &lt;a href="https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide"&gt;Sonatype OSS Maven Repository Usage Guide&lt;/a&gt;) y después de unas cuantas maldiciones más consigo desplegar unos SNAPSHOT e incluso una release. Y aquí llega el detalle: Como repositorio de versiones uso &lt;a href="http://mercurial.selenic.com/wiki/"&gt;Mercurial&lt;/a&gt;, para poder tener siempre el histórico conmigo, cosa muy útil hoy en día donde los repositoros públicos te pueden hacer alguna jugada, y para colmo de males desarrollo en Windows (ya, ya, nadie es perfecto) así que cuanto intento perfeccionar mi pom.xml para que no tenga ningún path absoluto, me encuentro con que la propiedad ${basedir}devuelve siempre el path en Windows con barras invertidas (backslahes) y que el modulo SCM no entiende la URL tal como se la pasa esa propiedad. Bueno,un detallito,  nada que Google no pueda arreglar, ¿verdad? Pues no, Google nos responde que ese problema es muy, pero que muy, común y que realmente "no hay solución" que no sea dar trescientas vueltas al mundo, constuir un plugin hacer unas cosas extrañísimas. Madre mía.&lt;br /&gt;&lt;br /&gt;Entonces decido que si no lo puedo sacar automáticamente, quizá si se lo paso en un fichero de propiedades y que cada usuario se apañe configurando.... ¡MEEEC! ¡Error! ¿A quien se le ocurriría poder leer algo que no esté en el pom.xml? Debe ser únicamente a los extraterrestres por que Maven lo vuelve a poner complicadísimo simplemente para leer un p|@#@# fichero de propiedades y usarlas en el pom.xml. Lo más aproximado a lo que quiero requiere un plugin que he encontrado, el cual está en estado alpha y ni siquiera en los repositorios oficiales...&lt;br /&gt;&lt;br /&gt;Así que sí, Maven permite hacer cosas "complicadísimas" de forma fácil por que te vienen hechas, pero algo tan "simple" como producir un artefacto que no sea .jar/.rar/.war/.ear, obtener un path normalizado o leer un fichero de propiedades es como para sacarse un master.&lt;br /&gt;&lt;br /&gt;¿Por qué es el mundo tan cruel?... ¿por qué?... ¿por qué? :(&lt;br /&gt;&lt;br /&gt;&lt;b&gt;//RANT OFF&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-2296118296776633090?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/2296118296776633090/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/06/maven-como-hacer-facil-lo-dificil-y.html#comment-form' title='7 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2296118296776633090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2296118296776633090'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/06/maven-como-hacer-facil-lo-dificil-y.html' title='Maven: como hacer fácil lo difícil y difícil lo fácil'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-6580169401506303388</id><published>2010-05-26T20:47:00.001+02:00</published><updated>2010-05-26T20:48:56.315+02:00</updated><title type='text'>Escogiendo números al azar o RTFA</title><content type='html'>Hoy un mensaje breve y al grano, para ilustrar la importancia de perder un rato investigando el API de Java, que es muy rico y uno de las mejores cosas que tiene el lenguaje (vale, no es perfecto y tiene algunas cagadas *ehem* Date *ehem* pero te da muchas cosas hechas).&lt;br /&gt;&lt;br /&gt;El problema planteado es el típico que sale habitualmente en foros de "&lt;i&gt;escoger X números entre Y posibles de forma aleatoria y sin repeticiones&lt;/i&gt;". Una especie de Lotería Primitiva, vamos. Las soluciones habituales suelen tender a usar Random para generar números de forma aleatoria y comprobar si el número ya lo tenemos o no, solución que por probabilidad no podemos asegurar cuanto tiempo se tirará ejecutándose, o meter todos los números en una lista, escogerlos aleatoriamente e irlos borrando etc.&lt;br /&gt;&lt;br /&gt;La segunda opción no es mala y al menos es "determinista*", pero si perdemos 5 minutos en el API de las colecciones, podemos llegar a esto (como extra devolvemos los números ordenados): &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;/**&lt;br /&gt;   * Devuelve numberCount números de entre 1 y&lt;br /&gt;   *  maxNumber escogidos de forma aleatoria y&lt;br /&gt;   * ordenados.&lt;br /&gt;   * &lt;br /&gt;   * @param maxNumber&lt;br /&gt;   * @param numberCount&lt;br /&gt;   * @return La lista ordenada con los números seleccionados aleatoriamente.&lt;br /&gt;   */&lt;br /&gt;  private static List selectRandomNumbers(int maxNumber, int numberCount)&lt;br /&gt;  {&lt;br /&gt;    List numberList = new LinkedList();&lt;br /&gt;    for (int i = 1; i &lt;= maxNumber; i++)&lt;br /&gt;    {&lt;br /&gt;      numberList.add(i); //**&lt;br /&gt;    }&lt;br /&gt;    Collections.shuffle(numberList);&lt;br /&gt;    numberList =&lt;br /&gt;      numberList.subList(0, numberCount);&lt;br /&gt;    Collections.sort(numberList);&lt;br /&gt;    return numberList;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;Y eso es todo. Dos métodos de Collections y uno de List hacen todo el trabajo y no tenemos que preocuparnos de nada. Los ingenieros del JDK se preocuparán de optimizar ese código y nosotros podemos dedicarnos al “core” de nuestro negocio.La moraleja es... no hagas tú el trabajo si alguien ya lo ha hecho por ti. O como decía Mulder cuando programaba en Java... "&lt;i&gt;La verdad, está en el API&lt;/i&gt;" ;P&lt;br /&gt;&lt;br /&gt;*: Determinista en cuanto sabemos el flujo de ejecución que va a seguir sin depender del azar.&lt;br /&gt;**: Sí, estoy metiendo primitivas en una lista, cosa que solo funciona gracias al mágico autoboxing de las últimas versiones de Java y que no me gusta, pero para este caso el compilador escribiría el mismo código, así que...&lt;br /&gt;&lt;br /&gt;Happy coding! EJ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-6580169401506303388?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/6580169401506303388/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/05/escogiendo-numeros-al-azar-o-rtfa.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6580169401506303388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/6580169401506303388'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/05/escogiendo-numeros-al-azar-o-rtfa.html' title='Escogiendo números al azar o RTFA'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-5231259222293890205</id><published>2010-05-07T21:07:00.001+02:00</published><updated>2010-05-07T21:08:13.383+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='desarrollo'/><title type='text'>Los criterios que NO deberías utilizar para escoger una solución</title><content type='html'>Hola,&lt;br /&gt;Hoy toca hablar de un tema interesante aunque menos técnico que los anteriores:&lt;i&gt; Criterios a la hora de escoger una solución&lt;/i&gt;. O mejor dicho, criterios que &lt;b&gt;no&lt;/b&gt; hay que seguir para escoger una solución.&lt;br /&gt;Me explico:&lt;br /&gt;En Internet y en Java en particular suele haber montones de herramientas, librerías y frameworks que se solapan en funciones y que se venden como "la solución a tu problema". Muchos desarrolladores tienen alergia a escoger cuando hay mucho donde elegir, siempre es más fácil cuando otra persona carga con la responsabilidad, y tienden a dejarse llevar por razonamientos simples que no suelen tener mucha base. Aunque es complicado decir cuales son las buenas razones para escoger una opción sobre otras, es relativamente fácil descartar algunas opciones populares, aunque no por ello menos erroneas.&lt;br /&gt;&lt;br /&gt;Así que sin más dilación, pasaremos una serie de argumentos en los cuales &lt;b&gt;NO&lt;/b&gt; te tendrías que basar para escoger una solución sobre otra:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Por que es nuevo&lt;/b&gt;: Éste es uno de los argumentos favoritos de los “entusiastas por las novedades”, para los cuales todo lo nuevo es muchísimo mejor que lo anterior. La demostración de la estupidez de semejante argumento es muy sencilla: Cualquier cosa, repito: cualquier cosa, cuando sale es nueva. Así que ser novedad es algo que todo el mundo tiene y que cura el tiempo independientemente de lo adecuada que sea o no una solución. &lt;br /&gt;&lt;li&gt;&lt;b&gt;Por que lo usa "todo el mundo"&lt;/b&gt;: Éste, en cambio, es el argumento favorito de la gente sin criterio que prefiere ir en medio de la manada, sin importarle si la manada se dirige a un pozo o no. Los males compartidos parecen menos males, ¿no? Pues no. Si lo usa todo el mundo, efectivamente hay mayores probabilidades de que te sirva, si eres como todo el mundo, pero no es una garantía y como dice el chiste: “¡Come mierda! ¡Millones de moscas no pueden estar equivocadas!” :). Ten en cuenta que hubo un tiempo donde “todo el mundo” hacia JSPs con EJB1.1 y CMP... auch.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Por que lo usa Google/EBay/Amazon...&lt;/b&gt;: Éste es un argumento parecido al anterior pero por el lado opuesto, apto para mentes que se sienten mejor si escogen lo mismo que “los grandes”. Desafortunadamente para nosotros, la mayoría no somos Google/Ebay etc. ni tenemos sus necesidades, recursos ni prioridades, así que usar una arquitectura pensada para recibir millones de visitas por segundo puede ser muy “cool”, pero seguramente sea una perdida de tiempo y un ejemplo claro de sobre-ingeniería. No hay nada de malo en no ser una de esas grandes compañías, millones de compañías no lo son, así que es mejor usar la cabeza y adaptarnos a NUESTRA situación.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Por que es caro&lt;/b&gt;:&lt;i&gt;... y como es caro es bueno&lt;/i&gt;. Sí, sí, ríete pero yo he estado presente en reuniones donde ese ha sido el único criterio de selección. Por otro lado, sólo hace falta estar como oyente, atónito pero oyente, en una reunión donde se fija el precio de un producto para darse cuenta de lo que éste significa: Era una herramienta que se vendió por cifras con 6 ceros y en € y el precio fue fijado después de un estudio sobre “el precio que los potenciales clientes estarían dispuestos a pagar”. Ni más ni menos.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Por que es diferente a lo anterior&lt;/b&gt;: Un argumento muy común, utilizado para “diferenciarse de la competencia” pero que, al fin y al cabo, por si mismo no dice nada sobre lo adecuado o no de una solución. Cabe preguntarse si el resto de soluciones que lo hacen de la otra forma son todas parte de una confabulación judeo-masónica, son tontos... ¿o quizá es por que si lo hacen así es por que es más adecuado? En fin, que la evolución es necesaria pero diferente no es necesariamente mejor.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Por que su publicidad dice que...&lt;/b&gt;: Ésta es un clásico y siempre me sorprende que gente por otro lado totalmente racional e inteligente sea tan “tonta” de creerse lo que dice la auto-publicidad. ¿Que esperan? ¿Que ellos mismos digan que su solución no es adecuada para todo el mundo o que es un asco? No debería ser necesario mencionarla... pero es que todavía la gente sigue cayendo en ella.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ul&gt;De momento esa es la lista de las razones más flagrantemente inválidas que se me ocurren, seguro que hay más pero estas son unos buenos ejemplos. Y &lt;b&gt;repito&lt;/b&gt;, no es que esas razones no signifiquen nada, es simplemente que sin otras razones para sustentar la elección, no significan mucho.&lt;br /&gt;&lt;br /&gt;Así que no os dejéis llevar y haced vuestras propias elecciones basadas en razonamientos un poco lógicos. Al fin y al cabo se supone que intentamos que ésto sea una ciencia, así que un poco de método científico no viene mal de vez en cuando.&lt;br /&gt;&lt;br /&gt;¡Hasta la próxima!&lt;br /&gt;&lt;br /&gt;EJ&lt;br /&gt;Happy Coding!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-5231259222293890205?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/5231259222293890205/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/05/los-criterios-que-no-deberias-utilizar.html#comment-form' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/5231259222293890205'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/5231259222293890205'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/05/los-criterios-que-no-deberias-utilizar.html' title='Los criterios que NO deberías utilizar para escoger una solución'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-2451213916072180971</id><published>2010-04-16T22:37:00.005+02:00</published><updated>2010-05-07T21:09:11.642+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Groovy'/><title type='text'>Cláusulas dinámicas en Groovy: versión a prueba de Inyección SQL</title><content type='html'>Hola,&lt;br /&gt;&lt;br /&gt;En una &lt;a href="/2010/03/un-poco-de-magia-groovy-para-construir.html"&gt;entrada anterior&lt;/a&gt; comentamos cómo crear dinámicamente una sentencia SQL partiendo de una lista de valores para una columna.&lt;br /&gt;Ya advertimos que debido al uso de la concatenación de cadenas, el método sólo era válido si teníamos totalmente controlados los parámetros de entrada, ya que si no podíamos sufrir un ataque de &lt;a href="http://es.wikipedia.org/wiki/Inyecci%C3%B3n_SQL"&gt;Inyección SQL&lt;/a&gt;, pero con un poco más de trabajo, podemos crear una solución que nos proteja contra estos ataques, y es la que presentamos ahora:&lt;br /&gt;Suponiendo la misma tabla que en la entrada anterior, lo que haremos en este caso es que la cadenas que vamos a concatenar colocará interrogantes (?) donde deberían ir los valores y luego pasaremos la lista de valores a la sentencia SQL para que la ejecute. Haciéndolo así Groovy usará un PreparedStatement para asignar los valores de los parámetros y estaremos protegidos contra un posible ataque de &lt;a href="http://es.wikipedia.org/wiki/Inyecci%C3%B3n_SQL"&gt;Inyección SQL&lt;/a&gt;. Veámoslo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: groovy"&gt;def parametrosMultiples = 'ABE,MUL,TAC,FRO' // o podría ser sólo 'ABE' o null&lt;br /&gt;def listaParametrosMultiples = parametrosMultiples.split(',')&lt;br /&gt;def condicionOR = listaParametrosMultiples.collect({'?'}).join(' OR TAB_CAMPO=')&lt;br /&gt;def clausulaWhere = parametrosMultiples ? "WHERE TAB_CAMPO=${condicionOR}" : ''&lt;br /&gt;def sentencia = "SELECT * FROM TAPP_TABLA ${clausulaWhere}" &lt;br /&gt;println sentencia&lt;/pre&gt;Resultado:&lt;br /&gt;&lt;pre class="brush: sql"&gt;SELECT * FROM TAPP_TABLA WHERE TAB_CAMPO=? OR TAB_CAMPO=? OR TAB_CAMPO=? OR TAB_CAMPO=?&lt;/pre&gt;&lt;br /&gt;Y al ejecutarlo, le pasamos la lista de valores como parámetros:&lt;br /&gt;&lt;pre class="brush: groovy"&gt;//...&lt;br /&gt;def sql = new Sql(ds); // ds es un DataSource que habremos obtenido de algún lado&lt;br /&gt;sql.eachRow( sentencia&lt;br /&gt;     ,Arrays.asList(listaParametrosMultiples)&lt;br /&gt;     ,{&lt;br /&gt;        ... // Aquí haremos algo para&lt;br /&gt;        // cada fila resultado (it)&lt;br /&gt;      }&lt;br /&gt;     );&lt;br /&gt;//...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Básicamente lo único que hemos hecho es usar la función &lt;i&gt;collect&lt;/i&gt; para sustituir los valores del array de parámetros por interrogantes, y luego convertir el array en una lista, con &lt;i&gt;Arrays.asList()&lt;/i&gt; para poder pasárselo al método &lt;i&gt;eachRow&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Con estos sencillos cambios, cerramos un posible agujero en la seguridad.&lt;br /&gt;&lt;br /&gt;Eso es todo de momento. ¡Feliz fin de semana!&lt;br /&gt;&lt;br /&gt;EJ&lt;br /&gt;Happy Coding!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-2451213916072180971?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/2451213916072180971/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/04/clausulas-dinamicas-en-groovy-version.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2451213916072180971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/2451213916072180971'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/04/clausulas-dinamicas-en-groovy-version.html' title='Cláusulas dinámicas en Groovy: versión a prueba de Inyección SQL'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-7864332227041709086</id><published>2010-03-20T21:13:00.005+01:00</published><updated>2010-03-21T17:00:36.461+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Ordenando listas de POJOs en Java por distintos criterios</title><content type='html'>Hoy un punto breve y simple para aquellos que siempre tienen dudas sobre como ordenar las colecciones que típicamente nos devuelven frameworks como Hibernate, JPA etc. Aunque muchas veces es mejor dejar que la BDD nos ordene directamente los registros en la consulta, a veces no es posible o por flexibilidad podemos desear hacerlo en el código.&lt;br /&gt;&lt;br /&gt;Dicho esto, pasemos al código de ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package test;&lt;br /&gt;&lt;br /&gt;import java.text.Collator;&lt;br /&gt;import java.util.Collections;&lt;br /&gt;import java.util.Comparator;&lt;br /&gt;import java.util.LinkedList;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.SortedSet;&lt;br /&gt;import java.util.TreeSet;&lt;br /&gt;&lt;br /&gt;public class App&lt;br /&gt;{&lt;br /&gt;  static class Item&lt;br /&gt;  {&lt;br /&gt;    private int codigo;&lt;br /&gt;    private String nombre;&lt;br /&gt;&lt;br /&gt;    public static Comparator COMPARADOR_POR_NOMBRE = new Comparator()&lt;br /&gt;    {&lt;br /&gt;      private Collator theC = Collator.getInstance();&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public int compare(Object o1, Object o2)&lt;br /&gt;      {&lt;br /&gt;        Item item1 = (Item) o1;&lt;br /&gt;        Item item2 = (Item) o2;&lt;br /&gt;        return theC.compare(item1.getNombre(), item2.getNombre());&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    public static Comparator COMPARADOR_POR_CODIGO = new Comparator()&lt;br /&gt;    {&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public int compare(Object o1, Object o2)&lt;br /&gt;      {&lt;br /&gt;        Item item1 = (Item) o1;&lt;br /&gt;        Item item2 = (Item) o2;&lt;br /&gt;        return item1.getCodigo() - item2.getCodigo();&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    public Item(int codigo, String nombre)&lt;br /&gt;    {&lt;br /&gt;      this.codigo = codigo;&lt;br /&gt;      this.nombre = nombre;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int getCodigo()&lt;br /&gt;    {&lt;br /&gt;      return codigo;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String getNombre()&lt;br /&gt;    {&lt;br /&gt;      return nombre;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public String toString()&lt;br /&gt;    {&lt;br /&gt;      return this.codigo + ": " + this.nombre;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public int hashCode()&lt;br /&gt;    {&lt;br /&gt;      final int prime = 31;&lt;br /&gt;      int result = 1;&lt;br /&gt;      result = prime * result + codigo;&lt;br /&gt;      return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public boolean equals(Object obj)&lt;br /&gt;    {&lt;br /&gt;      if (this == obj)&lt;br /&gt;        return true;&lt;br /&gt;      if (obj == null)&lt;br /&gt;        return false;&lt;br /&gt;      if (!(obj instanceof Item))&lt;br /&gt;        return false;&lt;br /&gt;      Item other = (Item) obj;&lt;br /&gt;      if (codigo != other.codigo)&lt;br /&gt;        return false;&lt;br /&gt;      return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) throws InterruptedException&lt;br /&gt;  {&lt;br /&gt;    List&lt;item&gt; listaDeItems = new LinkedList&lt;item&gt;();&lt;br /&gt;    listaDeItems.add(new Item(2, "Pedro"));&lt;br /&gt;    listaDeItems.add(new Item(1, "Juan"));&lt;br /&gt;    listaDeItems.add(new Item(3, "María"));&lt;br /&gt;    listaDeItems.add(new Item(0, "Paco"));&lt;br /&gt;    System.err.println("Lista tal cual\n-----------------");&lt;br /&gt;    for (Item unItem : listaDeItems)&lt;br /&gt;    {&lt;br /&gt;      System.err.println(".- " + unItem);&lt;br /&gt;    }&lt;br /&gt;    Collections.sort(listaDeItems&lt;br /&gt;                     ,Item.COMPARADOR_POR_NOMBRE);&lt;br /&gt;    System.err.println("Lista ordenada por nombre\n-----------------");&lt;br /&gt;    for (Item unItem : listaDeItems)&lt;br /&gt;    {&lt;br /&gt;      System.err.println(".- " + unItem);&lt;br /&gt;    }&lt;br /&gt;    Collections.sort(listaDeItems&lt;br /&gt;                     ,Item.COMPARADOR_POR_CODIGO);&lt;br /&gt;    System.err.println("Lista ordenada por codigo\n-----------------");&lt;br /&gt;    for (Item unItem : listaDeItems)&lt;br /&gt;    {&lt;br /&gt;      System.err.println(".- " + unItem);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Y la respuesta al ejecutar la aplicación sería:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Lista tal cual&lt;br /&gt;-----------------&lt;br /&gt;.- 2: Pedro&lt;br /&gt;.- 1: Juan&lt;br /&gt;.- 3: María&lt;br /&gt;.- 0: Paco&lt;br /&gt;Lista ordenada por nombre&lt;br /&gt;-----------------&lt;br /&gt;.- 1: Juan&lt;br /&gt;.- 3: María&lt;br /&gt;.- 0: Paco&lt;br /&gt;.- 2: Pedro&lt;br /&gt;Lista ordenada por codigo&lt;br /&gt;-----------------&lt;br /&gt;.- 0: Paco&lt;br /&gt;.- 1: Juan&lt;br /&gt;.- 2: Pedro&lt;br /&gt;.- 3: María&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El código ya es suficientemente explicativo, y únicamente resaltar el cuidado que hay que tener al implementar los comparadores ya que han de ser coherentes con el método equals, que no hay que olvidar implementar al igual que el método hashCode. Así que si según equals, dos objetos son iguales, el comparador ha de devolver 0. Ojo que esto significa que si esos dos objetos los metemos en una estructura que no permita repetidos, como por ejemplo ordenándolos a través de un SortedSet ,no importa lo que valgan el resto de campos que no se usan en el equals/compare, el objeto anterior desaparecerá. En este caso como usamos una lista durante todo el proceso, podemos meter objetos “repetidos” que no desaparecerán.&lt;br /&gt;&lt;br /&gt;¡Felices ordenaciones!&lt;br /&gt;&lt;br /&gt;EJ&lt;br /&gt;Happy Coding!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-7864332227041709086?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/7864332227041709086/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/ordenando-listas-de-pojos-en-java-por.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/7864332227041709086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/7864332227041709086'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/ordenando-listas-de-pojos-en-java-por.html' title='Ordenando listas de POJOs en Java por distintos criterios'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-8665379673140246608</id><published>2010-03-19T21:52:00.004+01:00</published><updated>2010-03-19T22:20:49.966+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Gestión simple de módulos y dependencias con java.util.ServiceLoader</title><content type='html'>En la entrada de hoy vamos a tratar un problema un poco más complejo, y por lo tanto la solución requiere también un poco más de trabajo, pero el resultado final merece la pena.&lt;br /&gt;&lt;br /&gt;En este caso el enunciado del problema sería algo tal que así:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Supongamos un proceso que puede  tener varias implementaciones diferentes y queremos permitir añadir  diferentes versiones y permitir escoger una implementación  simplemente a través de una cadena de configuración.&lt;/li&gt;&lt;/ul&gt;La solución que proponemos usa los mecanismos proporcionados en el API básico de Java (se necesita Java 6), sin acudir a soluciones más complejas como OsGI. En caso de necesitar poder actualizar/cambiar las dependencias en tiempo de ejecución y/o tener que poder permitir varias versiones de la misma dependencia a la vez en una misma JVM, sí deberíamos usar algo más complejo, pero si no es necesario, quizá no nos merezca la pena entrar en soluciones tan complejas.&lt;br /&gt;&lt;br /&gt;Volviendo a nuestra solución propuesta, la idea es utilizar la clase &lt;a href="http://java.util.serviceloader/"&gt;&lt;i&gt;java.util.ServiceLoader&lt;/i&gt;&lt;/a&gt; que nos permite declarar y recorrer las clases que implementan una Interfaz dada, pudiendo añadir nuevas implementaciones en el classpath simplemente empaquetando las clases adecuadamente.&lt;br /&gt;&lt;br /&gt;El primer punto a tener en cuenta es que podríamos cargar con el ServiceLoader directamente las implementaciones del proceso, sin embargo esta estrategia presenta un problema. ServiceLoader instancia las clases al recorrerlas buscando las implementaciones posibles, así que tenemos que tener toda las dependencias de todas las implementaciones posibles en el classpath o tendremos problemas. En algunos casos eso puede no ser problemático, pero imaginemos que tenemos varias soluciones y que unas usan Hibernate, otras JPA, otras JDO... al cargarlas todas tendríamos que tener todas las dependencias en el classpath, aunque sólo fuéramos a usar JDO, por ejemplo.  &lt;br /&gt;&lt;br /&gt;Así pues, lo que haremos será que la clase cargada a través de &lt;a href="http://java.util.serviceloader/"&gt;&lt;i&gt;ServiceLoader&lt;/i&gt;&lt;/a&gt; sea únicamente una utilidad que nos diga el nombre (una cadena) de clase que realmente implemente el proceso. De esta forma podemos cargar la clase utilidad tranquilamente en el classpath sin necesitar cargar todas las dependencias de la implementación.&lt;br /&gt;En la implementación necesitaremos:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;La interfaz a implementar por  todas las clases utilidad. Para hacerlo más flexible permitiremos  que una sola clase de utilidad pueda especificar varias  implementaciones, cada una con su “clave”:&lt;/li&gt;&lt;/ul&gt;&lt;pre class="brush: java"&gt;package my.pckg;&lt;br /&gt;&lt;br /&gt;public interface ProcessImplementationRegistration&lt;br /&gt;{&lt;br /&gt;  /*&lt;br /&gt;   Las implementaciones han de devolver una lista de pares clave/implementacion del proceso.&lt;br /&gt;   Por ejemplo: {{“jpa”,”com.my.JPAImplementation”},&lt;br /&gt;                 {“jdo”,”com.my.JDOImplementation”}}&lt;br /&gt;  */&lt;br /&gt;  public String[][] getProcessImplementations();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;En la clase que va a utilizar las  implementaciones, cargar las clases que utilizando el mecanismo de  la clase &lt;a href="http://java.util.serviceloader/"&gt;&lt;i&gt;java.util.ServiceLoader&lt;/i&gt;&lt;/a&gt;, y registrar las  implementaciones según su clave:&lt;/li&gt;&lt;/ul&gt;&lt;pre class="brush: java"&gt;ServiceLoader&lt;processimplementationregistration&gt; theServiceLoader = ServiceLoader.&lt;/processimplementationregistration&gt;&lt;i&gt;load&lt;/i&gt;(ProcessImplementationRegistration.class);&lt;br /&gt;    for(ProcessImplementationRegistration pirInstance: theServiceLoader)&lt;br /&gt;    {&lt;br /&gt;      // Usar pirInstance.getProcessImplementations() para registrar&lt;br /&gt;      // las implementaciones por clave&lt;br /&gt;      ...&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;Una vez hecho esto, para añadir una implementación simplemente tenemos  que crear la clase que implemente la interfaz my.pckg.ProcessImplementationRegistration, empaquetarla en un jar y  añadir en el directorio META-INF del jar, un fichero llamado  my.pckg.ProcessImplementationRegistration donde incluiremos el  nombre de la clase que implemente la interfaz.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Por ejemplo, clase que implementará la interfaz será:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package com.my;&lt;br /&gt;&lt;br /&gt;public class MyJPARegistration implements ProcessImplementationRegistration&lt;br /&gt;{&lt;br /&gt;  /*&lt;br /&gt;   Devolvemos la implementacion que registra este .jar&lt;br /&gt;  */&lt;br /&gt;  @Override&lt;br /&gt;  public String[][] getProcessImplementations()&lt;br /&gt;  {&lt;br /&gt;   return new String[][]{{“jpa”,”com.my.JPAImplementation”}}&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El fichero META-INF/my.pckg.ProcessImplementationRegistration contendrá únicamente la linea:&lt;br /&gt;&lt;pre class="brush: java"&gt;com.my.MyJPARegistration&lt;br /&gt;&lt;/pre&gt;y obviamente en el .jar incluiremos también la clase com.my.JPAImplementation y sus clases auxiliares necesarias si las hubiera.  &lt;br /&gt;&lt;br /&gt;Colocamos el .jar en el classpath, reiniciamos la maquina virtual Java y voilà: nuestra implementación será descubierta y clasificada según la clave “jpa”. Además, si no la usamos no hará falta que tengamos las clases de JPA en el classpath, por lo que podemos tener los .jar de registro de todas nuestras opciones y sólo necesitaremos tener en el classpath las dependencias de las que realmente usemos.&lt;br /&gt;&lt;br /&gt;Este es un mecanismo que utilizan algunos frameworks para incluir plugins opcionales de forma sencilla, basta incluir el fichero .jar en el classpath, sin necesitar incluir las dependencias a no ser que realmente los utilicemos.&lt;br /&gt;&lt;br /&gt;Eso es todo. No es una implementación completa pero espero haberos provisto de todos los detalles para que podáis experimentar vosotros mismos. Sólo hace falta tener Java 6, así que animaros a hacer alguna prueba.&lt;br /&gt;&lt;br /&gt;Un saludo y hasta la próxima.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-8665379673140246608?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/8665379673140246608/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/en-la-entrada-de-hoy-vamos-tratar-un.html#comment-form' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/8665379673140246608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/8665379673140246608'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/en-la-entrada-de-hoy-vamos-tratar-un.html' title='Gestión simple de módulos y dependencias con java.util.ServiceLoader'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-4201072465972791229</id><published>2010-03-18T18:14:00.002+01:00</published><updated>2010-03-19T22:21:05.429+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Groovy'/><title type='text'>Un poco de magia Groovy para construir clausulas SQL dinámicas</title><content type='html'>Para empezar, una entrada breve con un truco que aprendí/usé hace poco para construir una sentencia SQL dinámica usando Groovy. El problema reducido es el siguiente:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Dada una sentencia SQL que ha de hacer una comparación de una columna y cero, uno o varios valores, construir dinámicamente la sentencia teniendo en cuenta si se le pasa algún valor o varios.&lt;/li&gt;&lt;/ul&gt;Empezamos con la sentencia, que sería algo tal que así: &lt;br /&gt;&lt;pre class="brush: sql"&gt;SELECT * FROM TAPP_TABLA WHERE TAB_CAMPO='VALOR'&lt;/pre&gt;donde podemos pasar cero, uno o varios valores.&lt;br /&gt;&lt;br /&gt;La solución típica tiene unos cuantos if/else anidados, concatenaciones de cadenas etc. pero gracias a la mágia de Groovy, tenemos una solución sencilla y directa:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: groovy"&gt;def parametrosMultiples = 'ABE,MUL,TAC,FRO' // o podría ser sólo 'ABE' o null&lt;br /&gt;def condicionOR = parametrosMultiples?.split(',')?.join('\' OR TAB_CAMPO=\'')&lt;br /&gt;def clausulaWhere = parametrosMultiples ? "WHERE TAB_CAMPO='${condicionOR}'" : ''&lt;br /&gt;def sentencia = "SELECT * FROM TAPP_TABLA ${clausulaWhere}" &lt;br /&gt;println sentencia&lt;/pre&gt;&lt;br /&gt;Resultado:&lt;br /&gt;&lt;pre class="brush: sql"&gt;SELECT * FROM TAPP_TABLA WHERE TAB_CAMPO='ABE' OR TAB_CAMPO='MUL' OR TAB_CAMPO='TAC' OR TAB_CAMPO='FRO'&lt;/pre&gt;&lt;br /&gt;Paso a paso:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;En la primera linea: lo primero que hacemos es aplicar un &lt;b&gt;split&lt;/b&gt; a la cadena que nos llega, lo cual la separa en trozos y lo convierte en una lista de valores.&lt;/li&gt;&lt;li&gt;En la misma linea: acto seguido hacemos un &lt;b&gt;join&lt;/b&gt; de la lista lo cual nos vuelve a juntar todos los valores de la lista y los devuelve concatenados y separados por el separador que le indicamos. El separador está pensado para que enlace bien un valor con el siguiente, y la parte de inicio y fin se la pondremos después.&lt;/li&gt;&lt;li&gt;Resaltar que hemos añadido el interrogante &lt;b&gt;?.&lt;/b&gt; a ambas operaciones ya que si no lo hicieramos y el valor de la cadena &lt;i&gt;parametrosMultiples&lt;/i&gt; es null, saltaría una NullPointerException.&lt;/li&gt;&lt;li&gt;Para evitar añadir la clausula WHERE si no es necesario, usamos el operador de asignación condicional: &lt;i&gt;condición ? valorSiTrue : valorSiFalso&lt;/i&gt;.&lt;/li&gt;&lt;li&gt;Por último, interpolamos el valor de la variable clausulaWhere al final de la sentencia, haciendo uso de las facilidades de Groovy para introducir valores de variables en cadenas (usando dobles comillas y ${}).&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;Veremos que si cambiamos el valor de &lt;i&gt;parametrosMultiples&lt;/i&gt; por un valor simple o por null, la cadena devuelta es la correcta y no se produce ningún error.&lt;br /&gt;&lt;br /&gt;Bueno, espero que este truquillo os de alguna idea de como poder usar la magia de Groovy para hacer algunas cosas de forma más sencilla.&lt;br /&gt;&lt;br /&gt;¡Hasta la próxima!&lt;br /&gt;&lt;br /&gt;PD: Resaltar que este truco es válido si nosotros mismos controlamos los valores que puede tomar la variable &lt;i&gt;parametrosMultiples&lt;/i&gt;, ya que si no, al estar concatenando cadenas seríamos susceptibles a la &lt;a href="http://es.wikipedia.org/wiki/Inyecci%C3%B3n_SQL"&gt;Inyección SQL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-4201072465972791229?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/4201072465972791229/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/un-poco-de-magia-groovy-para-construir.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/4201072465972791229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/4201072465972791229'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/un-poco-de-magia-groovy-para-construir.html' title='Un poco de magia Groovy para construir clausulas SQL dinámicas'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-601687660178136429</id><published>2010-03-18T17:51:00.001+01:00</published><updated>2010-03-23T00:15:31.664+01:00</updated><title type='text'>Añadiendo coloreado de sintaxis a tus entradas de blog</title><content type='html'>Dado que me ha servido muy bien y como es de bien nacidos ser agradecidos, pongo un enlace a la entrada de blog que me ha ayudado a configurar este blog para poder utilizar sintaxis coloreada en mis ejemplos.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.cartercole.com/2009/10/awesome-syntax-highlighting-made-easy.html" title="code to do quick syntax highlighting"&gt;easy syntax highlighting for blogger&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;¡Gracias Carter!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-601687660178136429?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/601687660178136429/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/anadiendo-coloreado-de-sintaxis-tus.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/601687660178136429'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/601687660178136429'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/anadiendo-coloreado-de-sintaxis-tus.html' title='Añadiendo coloreado de sintaxis a tus entradas de blog'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-7476017802548873453</id><published>2010-03-18T14:32:00.000+01:00</published><updated>2010-03-18T14:32:52.767+01:00</updated><title type='text'>Caminante no hay camino, se hace camino al andar.</title><content type='html'>Con esta reseña comenzamos la andadura de este blog sobre Java y otros menestares, que espero sea, de vez en cuando, de la utilidad de alguien.&lt;br /&gt;&lt;br /&gt;Aquí van a caerse los truquillos que a veces descubro, las quejas que tengo y todo lo que me parezca. Todo lo reflejado en este blog será mi opinión personal, susceptible de ser equivocada pero mi opinión al fin y al cabo. Y las opiniones son como los culos, todo el mundo tiene uno y piensa que el de los demás apesta :). Así que si no te gusta, puedes razonar para intentar convencerme o puedes despotricar también, sólo que en este caso tu comentario seguramente no aparezca. Esto no es un medio público así que no tengo por que aguantar las chorradas de nadie, si quieres despotricar haz tu propio blog.&lt;br /&gt;&lt;br /&gt;Una vez dicho esto, espero no tener que recurrir mucho a la moderación, que se me cansa la muñeca y no la hinchable precisamente :P.&lt;br /&gt;&lt;br /&gt;¡Un saludo a todos!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-7476017802548873453?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/7476017802548873453/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/caminante-no-hay-camino-se-hace-camino.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/7476017802548873453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/7476017802548873453'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/03/caminante-no-hay-camino-se-hace-camino.html' title='Caminante no hay camino, se hace camino al andar.'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4898013526528408057.post-5379578490249909369</id><published>2010-01-18T21:16:00.000+01:00</published><updated>2011-06-01T14:40:49.606+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CMS Open Source'/><title type='text'>Para qué me sirve el Open Source</title><content type='html'>Saludos de nuevo,&lt;br /&gt;Después de una temporada algo liadillo, vuelvo a la carga con un tema sobre el que se pontifica mucho pero se habla poco del día a día, así que para dar algo de ejemplo voy a poner un par de ejemplos recientes para los que me ha servidor el concepto de Open Source. Ambos casos están relacionados, faltaría más, ya que se han dado en el último proyecto en el cual he estado envuelto y que ha visto la luz recientemente tras largos años de gestación.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Primer ejemplo&lt;/b&gt;: El proyecto en cuestión es "mecanizar" el web de una institución mediana/grande. Es decir, implantar un gestor de contenidos web (web CMS). Sobre el proyecto en cuestión ya hablaré en su día, si se tercia, pero el caso de uso viene dado por que después de mucho evaluar escogimos utilizar un software CMS Open Source.&lt;br /&gt;Antes de eso estuvimos a punto de implantar un CMS comercial y la diferencia fue abismal, ya que para hacer cualquier cosa teníamos que pasar por los "consultores autorizados" que te cobraban una pasta gansa por hacer lo mismo que le habían hecho a todos los demás clientes, pero como las soluciones no se comparten y para ellos es una ganga vender el mismo desarrollo X veces, pues que te voy a contar. No solo eso, además no había forma de saber si algo fallaba o no, si era una "feature" o un "bug", sin pasar por soporte, jugar al ping pong con los servicios externalizados que no tienen ni p*** idea más que seguir el guión de las preguntas que les han hecho etc. En fin, al menos en este tema (Webs CMS) quedamos bastante decepcionados del "estado del arte" en su vertiente comercial que más parece una tomadura de pelo que otra cosa.&lt;br /&gt;Después de mucho evaluar por aquí y por allá, decidimos utilizar un proyecto Open Source que se ajustaba bastante a nuestras necesidades y como tenemos personal con conocimientos para meterle mano (por eso lo escogimos implementado en Java) pues teníamos el convencimiento que podríamos adaptarlo.&lt;br /&gt;&lt;br /&gt;La parte positiva es que no nos equivocamos y hemos podido salir con ese producto, adaptándolo a nuestras necesidades y sabiendo donde falla y por qué, podemos planificar si queremos/debemos dedicar recursos a mejorarlo/arreglarlo o no. Que nadie se engañe, el coste de una solución así no es cero pero si se compara con el coste y relación precio/calidad de las soluciones en este tema en concreto...&lt;br /&gt;La parte negativa es que los administradores del proyecto van bastante a su bola e inexplicablemente hacen caso omiso a casi todas las sugerencias y mejoras que proponemos, incluyendo los "patch" para solventar errores que hemos detectado, algunos clamorosos. También hemos visto algunas cosas en el código por las que le cantaría las cuarenta, y ochenta, a alguno de mis programadores si las hicieran... pero bueno, al menos lo vemos y podemos arreglarlo si queremos.&lt;br /&gt;El problema es que nos vamos alejando tanto del original, pese a que intentamos mantenernos sincronizados con sus cambios, que ya casi tenemos un fork. Cuando haya un cambio de versión grande tendremos que plantearnos por donde seguir. Y para los que piensan que este dilema es exclusivo de los proyectos Open Source... el proyecto comercial que evaluamos primero acaba de cambiar de versión "grande" cuando hicimos el intento de adoptarlo y era totalmente incompatible con las anteriores, he hecho la versión anterior no era Java, y cuando lo dejamos ya tenían en beta la siguiente versión grande donde no abandonaban Java pero si lo rehacían todo y todos los componentes, adaptaciones y personalizaciones que te habían costado un ojo de la cara en consultores autorizados no servían... ¡Toma esa!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Segundo ejemplo&lt;/b&gt;: Dentro del mismo tema, uno de los problemas de un CMS para una web general medianamente decente es la integración de contenidos no-CMS. Es decir, aplicaciones externas que no se gestionan como contenidos exclusivamente CMS por algún motivo pero que han de salir publicadas "mezcladas con la información estática".&lt;br /&gt;Este tema lo hemos resuelto de varias formas, estudiando cada caso en concreto, y una de las soluciones para aplicaciones obsoletas que no se podían integrar o re-escribir es usar un portlet que haga de proxy y te permita incrustar la aplicación en tus páginas del CMS sin tener que tocar la aplicación... o no mucho. En este caso el problema fue que investigando, investigando, el tema está bastante dejado y la mayoría de implementaciones de un portlet proxy son simples juguetes o "abandonware" (proyectos que ya no se actualizan desde hace años). Pero como son Open Source... descargamos uno de los proyectos Open Source, el que más prometía, lo re-montamos a partir de los trozos que ofrecía y con la escasa documentación que tenía, le arreglamos cuatro cosas que no funcionaban y pudimos usarlo.&lt;br /&gt;&lt;br /&gt;Ahora que hemos salido, podemos decir que la decisión en sí no fue mala, pero ha habido que mejorarlo bastante y tiene algunos puntos oscuros que no nos gustan mucho, por lo que al final no se parecerá mucho al original, pero el caso es que nos sirvió, funciona, pudimos adaptarlo a nuestras necesidades y de hecho ahora lo hemos adoptado :). Una vez lo tengamos claro y lo re-hagamos, esperamos poder re-abrirlo para que le sirva a alguien más, que es para lo que sirve en Open Source.&lt;br /&gt;&lt;br /&gt;En fin, yo soy el primer&amp;nbsp;escéptico que no se traga eso de que Open Source = bueno &lt;i&gt;per se&lt;/i&gt;&amp;nbsp;y sé lo que implica y cuesta mirar el código de otra gente, adaptarlo etc. Pero al mismo tiempo hay que darle al cesar lo que es del cesar y sabiendo usarlo, es una muy buena opción para algunos temas. Hablando de casos de uso reales, sus pros y sus contras, aprenderemos entre todos cuando y como usarlo mejor. La mera retórica o los "&lt;i&gt;proyectos de Powerpoint&lt;/i&gt;" no hacen funcionar las cosas en el mundo real.&lt;br /&gt;&lt;br /&gt;Happy coding! EJ&lt;br /&gt;&lt;br /&gt;PD: Si alguien tiene curiosidad y le pica saber nombres de productos/proyectos... como dice el dicho: "Se dice el pecado pero no el pecador" ;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4898013526528408057-5379578490249909369?l=lospuntossobrelasjotas.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lospuntossobrelasjotas.blogspot.com/feeds/5379578490249909369/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/11/para-que-me-sirve-el-open-source.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/5379578490249909369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4898013526528408057/posts/default/5379578490249909369'/><link rel='alternate' type='text/html' href='http://lospuntossobrelasjotas.blogspot.com/2010/11/para-que-me-sirve-el-open-source.html' title='Para qué me sirve el Open Source'/><author><name>ElJavato</name><uri>http://www.blogger.com/profile/12080700942637721361</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
