<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>Blog thuest</title>
	<atom:link href="http://blog.thuest.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.thuest.com</link>
	<description>Blog oficial de thuest.com</description>
	<pubDate>Thu, 05 Nov 2009 05:19:54 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>es</language>
			<item>
		<title>Seleccionados para el Seedrocket Madrid Stage</title>
		<link>http://blog.thuest.com/2009/11/05/seleccionados-para-el-seedrocket-madrid-stage/</link>
		<comments>http://blog.thuest.com/2009/11/05/seleccionados-para-el-seedrocket-madrid-stage/#comments</comments>
		<pubDate>Thu, 05 Nov 2009 05:19:54 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Thuest]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=40</guid>
		<description><![CDATA[El título del post lo dice todo: Thuest es uno de los 16 proyectos seleccionados para participar en la primera edición del Seedrocket Madrid Stage. El premio es poder asistir a diversas ponencias durante 3 días, recibir consejos de los mentores y la posibilidad de presentar el proyecto ante inversores.
Además asistiré a las sesiones magistrales [...]]]></description>
			<content:encoded><![CDATA[<p>El título del post lo dice todo: Thuest es uno de <a title="Finalistas del Seedrocket Madrid Stage" href="http://www.seedrocket.com/madridstage/2009/11/finalistas-del-seedrocket-madrid-stage/">los 16 proyectos seleccionados</a> para participar en la primera edición del <a title="Seedrocket Madrid Stage" href="http://www.seedrocket.com/madridstage/">Seedrocket Madrid Stage</a>. El premio es poder asistir a diversas ponencias durante 3 días, recibir consejos de los mentores y la posibilidad de presentar el proyecto ante inversores.</p>
<p>Además asistiré a las <a title="Sesiones magistrales" href="http://www.seedrocket.com/madridstage/2009/11/sesiones-magistrales-en-el-seedrocket-stage/">sesiones magistrales</a> impartidas después de las ponencias, donde algunos emprendedores de Internet con éxito nos hablarán de temas cruciales.</p>
<p>El <a title="Programa Seedrocket Madrid Stage" href="http://www.seedrocket.com/madridstage/SeedRocket_MadridStage_Programa.pdf">programa completo</a> ya es público y reconozco que en principio me gusta y me ilusiona (sí, así de bobo soy). Pase lo que pase, eso que me llevo. La semana que viene os contaré como ha ido.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/11/05/seleccionados-para-el-seedrocket-madrid-stage/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Lanzado Thuest 0.4</title>
		<link>http://blog.thuest.com/2009/11/04/lanzado-thuest-04/</link>
		<comments>http://blog.thuest.com/2009/11/04/lanzado-thuest-04/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 11:40:51 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Thuest]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=39</guid>
		<description><![CDATA[Intentando cumplir con el compromiso adquirido aquí está la nueva versión de Thuest. La verdad es que estas dos semanas no han sido tan productivas como las anteriores, pero las circunstancias no han permitido otra cosa.
Los cambios en esta versión se limitan a pequeñas correciones internas, que afectan mínimamente al usuario final. Las nuevas características [...]]]></description>
			<content:encoded><![CDATA[<p>Intentando cumplir con el compromiso adquirido aquí está la nueva versión de <a title="Thuest" href="http://thuest.com">Thuest</a>. La verdad es que estas dos semanas no han sido tan productivas como las anteriores, pero las circunstancias no han permitido otra cosa.</p>
<p>Los cambios en esta versión se limitan a pequeñas correciones internas, que afectan mínimamente al usuario final. Las nuevas características que tenía planeadas para esta versión las trasladaré a la siguiente y asunto resuelto. La ventaja de crear tu propio producto <img src='http://blog.thuest.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Ya estoy trabajando en un par de ideas que van a mejorar mucho la funcionalidad de la aplicación, espera un par de semanas y verás como la versión 0.5 sí que vale la pena.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/11/04/lanzado-thuest-04/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Ejecutar un subconjunto de pruebas en Grails</title>
		<link>http://blog.thuest.com/2009/10/19/ejecutar-un-subconjunto-de-pruebas-en-grails/</link>
		<comments>http://blog.thuest.com/2009/10/19/ejecutar-un-subconjunto-de-pruebas-en-grails/#comments</comments>
		<pubDate>Mon, 19 Oct 2009 14:55:45 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Grails]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=38</guid>
		<description><![CDATA[El comando para ejecutar las pruebas en Grails es el siguiente:

&#62;$ grails test-app

Con este comando ejecutamos todas las pruebas que tengamos definidas, las unitarias y las de integración. Cuando estás codificando las pruebas unitarias y quieres lanzarlas poco a poco resulta molesto tener que esperar a que arranque todo el framework y que se ejecuten [...]]]></description>
			<content:encoded><![CDATA[<p>El comando para ejecutar las pruebas en <a title="Grails" href="http://grails.org">Grails</a> es el siguiente:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&gt;</span>$ grails test-app</pre></div></div>

<p>Con este comando ejecutamos <strong>todas</strong> las pruebas que tengamos definidas, las unitarias y las de integración. Cuando estás codificando las pruebas unitarias y quieres lanzarlas poco a poco resulta molesto tener que esperar a que arranque todo el <em>framework</em> y que se ejecuten también las pruebas de integración.</p>
<p>Si quieres ejecutar sólo las <a href="http://grails.org/Unit+Testing" title="Pruebas unitarias en Grails">pruebas unitarias</a> ejecuta el siguiente comando:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&gt;</span>$ grails test-app <span style="color: #660033;">-unit</span></pre></div></div>

<p>Para ejecutar sólo las pruebas de integración:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&gt;</span>$ grails test-app <span style="color: #660033;">-integration</span></pre></div></div>

<p>Y en cualquier caso, si quieres filtrar para ejecutar sólo las pruebas que has definido en un fichero, añade el nombre de dicho fichero al final de la línea de comandos (sin el sufijo &#8220;<code>Tests</code>&#8220;):</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&gt;</span>$ grails test-app <span style="color: #660033;">-integration</span> MisPruebas
<span style="color: #000000; font-weight: bold;">&gt;</span>$ grails test-app <span style="color: #660033;">-unit</span> MisPruebas
<span style="color: #000000; font-weight: bold;">&gt;</span>$ grails test-app MisPruebas</pre></td></tr></table></div>

<p>El comando de la línea 1 ejecutaría las pruebas de integración definidas en el fichero <code>MisPruebasTests.groovy</code>. El comando de la línea 2 ejecutaría las pruebas unitarias definidas en el fichero <code>MisPruebasTests.groovy</code>, y el tercer comando ejecutaría todas las pruebas que hallara en un fichero con nombre <code>MisPruebasTests.groovy</code>. Recuerda que grails distingue entre las pruebas unitarias y las de integración en función de la carpeta en la que estén colocados los ficheros de prueba: <code>test/unit</code> para las primeras y <code>test/integration</code> para las segundas.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/10/19/ejecutar-un-subconjunto-de-pruebas-en-grails/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Lanzado Thuest 0.3</title>
		<link>http://blog.thuest.com/2009/10/17/lanzado-thuest-03/</link>
		<comments>http://blog.thuest.com/2009/10/17/lanzado-thuest-03/#comments</comments>
		<pubDate>Fri, 16 Oct 2009 23:49:24 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Thuest]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=25</guid>
		<description><![CDATA[Release early, release often. Aunque por ahora mi desarrollo no tiene mucho que ver con el que comenzó Linus en su momento, creo que sacar nuevas versiones cada poco tiempo es beneficioso. Me he marcado un ritmo de 2 semanas por versión y lo estoy siguiendo bastante bien. Hoy he desplegado Thuest 0.3, respetando los [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Release early, release often en inglés" href="http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html">Release early</a>, <a title="Release early, release often en español" href="http://es.tldp.org/Otros/catedral-bazar/cathedral-es-paper-04.html">release often</a>. Aunque por ahora mi desarrollo no tiene mucho que ver con el que comenzó Linus en su momento, creo que sacar nuevas versiones cada poco tiempo es beneficioso. Me he marcado un ritmo de 2 semanas por versión y lo estoy siguiendo bastante bien. Hoy he desplegado <a title="Thuest" href="http://thuest.com">Thuest 0.3</a>, respetando los (pocos) datos de usuarios que tenía de la versión anterior.</p>
<p>Además de seguir corrigiendo errores que detecto constatemente, las principales novedades de esta versión son:</p>
<ul>
<li>Posibilidad de mostrar todos los <em>checkpoints</em> de un <em>quest</em> desde el comienzo, nada de tener que superarlos secuencialmente.</li>
<li>Tags para los <em>quests</em>.</li>
<li>Mayor integración con <a title="Twitter" href="http://twitter.com">twitter</a>.</li>
<li>Más facilidad para añadir <em>quests</em> a grupos de <em>quests</em> y a la listas de objetivos de los clanes.</li>
<li>Exportación en <abbr title="Comma-Separated Values">CSV</abbr> de datos estadísticos de los <em>quests</em>.</li>
</ul>
<p>Continúa leyendo el post para ver los detalles de cada punto.</p>
<p><span id="more-25"></span></p>
<h4>Posibilidad de mostrar todos los <em>checkpoints</em> de un <em>quest</em> desde el comienzo</h4>
<p>Hasta ahora los <em>checkpoints</em> debían ser superados por los jugadores en el orden que el creador los había colocado. No existía la posibilidad de intentar resolver un <em>checkpoint</em> posterior si no se habían superado con éxito todos los anteriores. La alternativa que se presenta a partir de esta versión es la de ofrecer a los participantes todos los <em>checkpoints</em> desde el principio, permitiendo que intenten resolverlos en el orden que quieran. Los creadores podrán configurar este aspecto en el momento de crear los <em>quests</em>.</p>
<p>Si se hace uso de esta nueva funcionalidad, al acceder a la página del <em>quest</em> los participantes verán algo parecido a lo mostrado en la siguiente imagen.</p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/10/checkpoints_allin.png"><img class="aligncenter size-full wp-image-28" title="Todos los checkpoints" src="http://blog.thuest.com/wp-uploads/2009/10/checkpoints_allin.png" alt="Todos los checkpoints" /></a></p>
<p>La columna &#8220;Estado&#8221; muestra algunos iconos que representan información intesante:</p>
<ul  style="list-style-type: none;">
<li><img class="alignnone size-full wp-image-29" title="Barra azul" src="http://blog.thuest.com/wp-uploads/2009/10/bar_blue.png" alt="Barra azul" width="16" height="16" /> El usuario no ha superado aún el checkpoint.</li>
<li><img class="alignnone size-full wp-image-30" title="Tick" src="http://blog.thuest.com/wp-uploads/2009/10/tick.png" alt="Tick" width="16" height="16" /> El usuario ha superado el checkpoint.</li>
<li><img class="alignnone size-full wp-image-31" title="Prohibido" src="http://blog.thuest.com/wp-uploads/2009/10/delete.png" alt="Prohibido" width="16" height="16" /> El usuario no ha superado aún el checkpoint y ha sobrepasado el número máximo de intentos, por lo que deberá esperar un tiempo para poder volver a intentarlo.</li>
</ul>
<h4>Tags para los <em>quests</em></h4>
<p>Los creadores podrán asociar a cada uno de sus <em>quests</em> un conjunto de <em>tags</em> o etiquetas. Estos conjuntos podrán ser editados en cualquier momento por los creadores desde la página de edición del <em>quest</em>.</p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/10/tags.png"><img class="aligncenter size-full wp-image-32" title="Edición de tags" src="http://blog.thuest.com/wp-uploads/2009/10/tags.png" alt="Edición de tags" width="373" height="112" /></a></p>
<p>Cualquier usuario podrá acceder al conjunto de <em>quests</em> etiquetados con un determinado <em>tag</em> a través de URL&#8217;s sencillas de recordar similares a <a title="Quests con el tag 'el-tag'" href="http://thuest.com/tag/el-tag">http://thuest.com/tag/el-tag</a></p>
<p>Los <em>tags</em> serán visibles en las páginas de los <em>quests</em> y en la página de inicio de sesión, donde se mostrará un subconjunto de los <em>tags</em> empleados en toda la aplicación.</p>
<h4>Mayor integración con twitter</h4>
<p><a href="http://blog.thuest.com/wp-uploads/2009/10/mas_twitter.png"><img class="aligncenter size-full wp-image-33" title="Configuración Twitter" src="http://blog.thuest.com/wp-uploads/2009/10/mas_twitter.png" alt="Configuración Twitter" /></a></p>
<p>Ahora es posible recibir automáticamente notificaciones en twitter mediante <em>Direct Messages</em>, con información sobre eventos que pueden afectar al usuario, como la recepción de un mensaje privado de otro usuario, la republicación de un <em>quest</em> en el que participaba, la definición de un nuevo <em>quest</em> objetivo en un clan del que forma parte, etc.</p>
<p>Además, es posible mostrar en la página del perfil público del usuario el <em>username</em> de twitter.</p>
<h4>Más facilidad para añadir <em>quests</em> a grupos de <em>quests</em> y a la listas de objetivos de los clanes</h4>
<p>Este tema pedía una mejora como agua de Mayo. Ahora se proporciona un simple buscador con el que localizar los <em>quests</em> que estamos buscando. Una vez obtenidos los resultados, simplemente pulsando el icono <img class="alignnone size-full wp-image-34" title="Seleccionar quest" src="http://blog.thuest.com/wp-uploads/2009/10/add.png" alt="Seleccionar quest" width="16" height="16" /> el <em>quest</em> pasará a formar parte de nuestro grupo de <em>quests</em> o de la lista de objetivos para nuestro clan.</p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/10/add_quests.png"><img class="aligncenter size-full wp-image-35" title="Buscar y selecionar quest" src="http://blog.thuest.com/wp-uploads/2009/10/add_quests.png" alt="Buscar y selecionar quest" /></a></p>
<h4>Exportación en <abbr title="Comma-Separated Values">CSV</abbr> de datos estadísticos de los <em>quests</em></h4>
<p>Además de poder salvar los gráficos como imágenes ahora también es posible exportar los datos en formato <a title="Comma-Separated Values en la Wikipedia" href="http://es.wikipedia.org/wiki/CSV">CSV</a>, lo cual facilitará su análisis <em>offline</em>.</p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/10/csv.png"><img class="aligncenter size-full wp-image-36" title="Exportar en CSV" src="http://blog.thuest.com/wp-uploads/2009/10/csv.png" alt="Exportar en CSV" width="344" height="242" /></a></p>
<h4>Para ti&#8230;</h4>
<p>Para terminar, de nuevo el avance en la definición y resolución de los <em>issues</em> asociados a esta versión. Para los morbosos.</p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/10/progress.png"><img class="aligncenter size-full wp-image-37" title="Progreso del desarrollo en la versión 0.3" src="http://blog.thuest.com/wp-uploads/2009/10/progress.png" alt="Progreso del desarrollo en la versión 0.3" /></a></p>
<p>Y ahora a por Thuest 0.4!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/10/17/lanzado-thuest-03/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Probando URL Mappings en Grails</title>
		<link>http://blog.thuest.com/2009/10/14/probando-url-mappings-en-grails/</link>
		<comments>http://blog.thuest.com/2009/10/14/probando-url-mappings-en-grails/#comments</comments>
		<pubDate>Wed, 14 Oct 2009 13:40:43 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Grails]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=24</guid>
		<description><![CDATA[Llegó el momento de las pruebas, no se pueden posponer más. En Grails existía un plugin que facilitaba mucho esto de las pruebas unitarias y de integración. Facilitaba tanto la labor que a partir de la versión 1.1 decicieron integrarlo en la rama principal de Grails. Y yo que me alegro porque merece la pena.
Son [...]]]></description>
			<content:encoded><![CDATA[<p>Llegó el momento de las pruebas, no se pueden posponer más. En <a title="Grails" href="http://grails.org">Grails</a> existía un plugin que facilitaba mucho esto de las pruebas unitarias y de integración. Facilitaba tanto la labor que a partir de la versión 1.1 decicieron <a title="Plugin Testing para Grails" href="http://grails.org/Testing+Plugin">integrarlo</a> en la rama principal de Grails. Y yo que me alegro porque merece la pena.</p>
<p>Son muchas las cosas que podemos probar y Grails nos ayuda en muchos temas específicos proporcionándonos clases de las que extender para desarrollar nuestros tests. Tendremos que dejar nuestras pruebas en los directorios <code>test/integration</code> o <code>test/unit</code>, siguiendo la jerarquía de paquetes de los elementos a probar y heredando de determinadas clases en función de lo que estemos probando:</p>
<ul>
<li>Clases de dominio: heredar <a title="Documentación de GrailsUnitTestCase" href="http://grails.org/doc/latest/api/grails/test/GrailsUnitTestCase.html">grails.test.GrailsUnitTestCase</a></li>
<li><a title="Probar controladores" href="http://grails.org/Testing+Controllers">Controladores</a>: heredar grails.test.ControllerUnitTestCase</li>
<li>Tag libs propias: heredar grails.test.TagLibUnitTestCase</li>
<li>Servicios: heredar <a title="Documentación de GrailsUnitTestCase" href="http://grails.org/doc/latest/api/grails/test/GrailsUnitTestCase.html">grails.test.GrailsUnitTestCase</a></li>
<li>URL Mappings: heredar <a title="Documentación de GrailsUrlMappingsUnitTestCase" href="http://grails.org/doc/latest/api/grails/test/GrailsUrlMappingsTestCase.html">grails.test.GrailsUrlMappingsTestCase</a></li>
</ul>
<p>Para lanzar las pruebas deberemos ejecutar la siguiente orden desde la línea de comandos:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$<span style="color: #000000; font-weight: bold;">&gt;</span> grails test-app</pre></div></div>

<p>En futuros posts iré hablando del resto de pruebas, pero ahora me centraré en las relacionadas con los mapeos de URL&#8217;s.</p>
<p><span id="more-24"></span></p>
<p>Por defecto los <a title="URL mappings en Grails" href="http://grails.org/URL+mapping">mapeos de URL&#8217;s en Grails</a> se declaran en el fichero <code>grails-app/conf/UrlMappings.groovy</code>. Se pueden hacer muchas virguerías, pero eso lo dejamos para otra ocasión. Las pruebas que vamos a desarrollar tienen como objetivo <strong>asegurar el correcto funcionamiento de dos procesos</strong> que tendrán lugar en nuestra aplicación constatemente:</p>
<ol>
<li>Extraer de la URL que llega al servidor en una petición los siguientes elementos: controlador, acción y parámetros.</li>
<li>Formar URL&#8217;s mediante algunos de los tags proporcionados por Grails, como <a title="Tag link" href="http://grails.org/Tag+-+link"><code>&lt;g:link&gt;</code></a> y <a title="Tag form" href="http://grails.org/Tag+-+form"><code>&lt;g:form&gt;</code></a></li>
</ol>
<p>Es necesario que nuestros tests sean de <strong>integración</strong>, pues necesitamos que todo el entorno de Grails esté operativo. Al fin y al cabo son sus librerías las que se encargan mágicamente de los dos puntos anteriores. Por lo tanto nuestros tests irán en la carpeta <code>test/integration</code>. Si hemos respetado el nombre del fichero de mapeos usado por defecto, bastará con que coloquemos en este directorio un fichero con el nombre <code>UrlMappingsTest.groovy</code>. En este fichero, todos <strong>los métodos que comiencen por <code>test</code> serán considerados pruebas</strong> y se ejecutarán una detrás de otra. <strong>Los demás métodos se ignorarán</strong>. Es posible definir los clásicos métodos <code>setUp</code> y <code>tearDown</code>, que se ejecutarán antes y después de cada prueba respectivamente.</p>
<p>Podemos probar por separado cada uno de los puntos antes comentados, usando el método <code>assertForwardUrlMapping</code> para el primer caso y el método <code>assertReverseUrlMapping</code> para el segundo. Pero en general podemos <strong>usar el método <code>assertUrlMapping</code> para ejecutar los dos tipos de prueba</strong>.</p>
<p>Utilizaré un ejemplo real de <a title="Thuest" href="http://thuest.com">Thuest</a> para ilustrar la explicación. Tenemos los siguientes mapeos definidos:</p>

<div class="wp_syntax"><div class="code"><pre class="groovy" style="font-family:monospace;"><span style="color: #ff0000;">&quot;/quest/$idText?&quot;</span><span style="color: #66cc66;">&#123;</span>
    controller <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;quest&quot;</span>
    action <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;show&quot;</span>
    constraints <span style="color: #66cc66;">&#123;</span>
        idText<span style="color: #66cc66;">&#40;</span>validator:<span style="color: #66cc66;">&#123;</span><span style="color: #000000; font-weight: bold;">return</span> <span style="color: #66cc66;">!</span><span style="color: #66cc66;">&#40;</span>it <span style="color: #b1b100;">in</span> CH.<span style="color: #006600;">config</span>.<span style="color: #006600;">theq</span>.<span style="color: #006600;">urlMappingExceptions</span>.<span style="color: #006600;">quest</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#125;</span><span style="color: #66cc66;">&#41;</span>
    <span style="color: #66cc66;">&#125;</span>
<span style="color: #66cc66;">&#125;</span>
<span style="color: #ff0000;">&quot;/quest/$action?/$idText?&quot;</span><span style="color: #66cc66;">&#123;</span>
    controller <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;quest&quot;</span>
<span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>Con el primer mapeo, para todas las peticiones del estilo <code>/quest/XXX</code> se ejecutará la acción <code>show</code> del controlador <code>quest</code>, almacenando en <code>params.idText</code> el valor <code>XXX</code> como un String. Hay un <strong>restricción</strong>, según la cual cuando <code>XXX</code> sea alguno de los valores definidos en una lista que tengo declarada en la configuración de Grails (archivo <code>grails-app/conf/Config.groovy</code>), el mapeo no será válido, forzando a Grails a buscar otro mapeo.</p>
<p>El segundo mapeo es más genérico y sirve para capturar URL&#8217;s como <code>/quest/listAsPlayer/user1</code>.</p>
<p>La prueba que yo he definido para estos mapeos es la siguiente:</p>

<div class="wp_syntax"><div class="code"><pre class="groovy" style="font-family:monospace;"><span style="color: #993333;">void</span> testQuestMappings<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
    assertUrlMapping<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/quest&quot;</span>, controller: <span style="color: #ff0000;">&quot;quest&quot;</span>, action: <span style="color: #ff0000;">&quot;show&quot;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
        idText <span style="color: #66cc66;">=</span> <span style="color: #000000; font-weight: bold;">null</span>
    <span style="color: #66cc66;">&#125;</span>
    assertUrlMapping<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/quest/test&quot;</span>, controller: <span style="color: #ff0000;">&quot;quest&quot;</span>, action: <span style="color: #ff0000;">&quot;show&quot;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
        idText <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;test&quot;</span>
    <span style="color: #66cc66;">&#125;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">// Testing strings that mustn't be idText, because they are actions</span>
    CH.<span style="color: #006600;">config</span>.<span style="color: #006600;">theq</span>.<span style="color: #006600;">urlMappingExceptions</span>.<span style="color: #006600;">quest</span>.<span style="color: #663399;">each</span> <span style="color: #66cc66;">&#123;</span>action<span style="color: #66cc66;">-&amp;</span>gt<span style="color: #66cc66;">;</span>
        assertUrlMapping<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/quest/${action}&quot;</span>, controller: <span style="color: #ff0000;">&quot;quest&quot;</span>, action:action<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
            idText <span style="color: #66cc66;">=</span> <span style="color: #000000; font-weight: bold;">null</span>
        <span style="color: #66cc66;">&#125;</span>
        assertUrlMapping<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/quest/${action}/test&quot;</span>, controller: <span style="color: #ff0000;">&quot;quest&quot;</span>, action:action<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
            idText <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;test&quot;</span>
        <span style="color: #66cc66;">&#125;</span>
    <span style="color: #66cc66;">&#125;</span>
<span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>Los parámetros que recibe el método <code>assertUrlMapping</code> son:</p>
<ul>
<li>Una URL o un código HTTP (por ejemplo 500 para casos de error o 404 para recursos no encontrados)</li>
<li><code>controller</code>: nombre del controlador en minúsculas</li>
<li><code>action</code>: nombre de una acción</li>
<li><code>view</code>: nombre de una vista</li>
<li><code>paramAssertions</code>: una closure con parámetros que esperaríamos tener en la acción (proceso <code>ForwardUrlMapping</code>) o que proporcioamos a tags como <code>&lt;g:link&gt;</code> y <code>&lt;g:form&gt;</code> para obtener una URL válida (proceso <code>ReverseUrlMapping</code>)</li>
</ul>
<p>Lo primero que pruebo (<strong>líneas 2-4</strong>) es que cuando el usuario solicite la URL <code>/quest</code> se ejecutará la acción <code>show</code> del controlador <code>quest</code>, y que el parámetro <code>params.idText</code> será <code>null</code>. Ese caso yo lo tengo contemplado en mi acción, de forma que cuando <code>params.idText</code> no tiene valor hago una redicción a una página por defecto e informo de la situación anormal al usuario.</p>
<p>Al mismo tiempo estoy probando el <code>ReverseUrlMapping</code>, es decir, supongamos que tengo alguno de estos dos tags:</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;g:link</span> <span style="color: #000066;">action</span>=<span style="color: #ff0000;">&quot;quest&quot;</span> <span style="color: #000066;">controller</span>=<span style="color: #ff0000;">&quot;show&quot;</span> <span style="color: #000066;">params</span>=<span style="color: #ff0000;">&quot;[idText:null]&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;g:link</span> <span style="color: #000066;">action</span>=<span style="color: #ff0000;">&quot;quest&quot;</span> <span style="color: #000066;">controller</span>=<span style="color: #ff0000;">&quot;show&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></div></div>

<p>Con mi test me aseguro de que la URL generada es <code>/quest</code>. Otra cosa es que esto tenga sentido. En mi caso debo controlar que nunca se genere esta URL porque no tiene sentido, pero si por algún casual mis controles son superados siempre se generará una URL válida.</p>
<p>La siguiente comprobación (<strong>líneas 5-7</strong>) me asegura que cuando la URL solicitada sea del estilo <code>/quest/XXX</code> se ejecute la acción <code>show</code> del controlador <code>quest</code> y que <code>params.idText</code> tenga el valor <code>XXX</code>.</p>
<p>Igual que en el caso anterior, también se comprueba que el <code>ReverseUrlMapping</code> es correcto, lo cual implica que si tengo el siguiente tag:</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;g:link</span> <span style="color: #000066;">action</span>=<span style="color: #ff0000;">&quot;quest&quot;</span> <span style="color: #000066;">controller</span>=<span style="color: #ff0000;">&quot;show&quot;</span> <span style="color: #000066;">params</span>=<span style="color: #ff0000;">&quot;[idText:'mi-quest']&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></div></div>

<p>La URL que se generará será <code>/quest/mi-quest</code>. Perfecto.</p>
<p>El siguiente fragmento (<strong>líneas 10-16</strong>) se encarga de comprobar el correcto funcioamiento del mapeo para las excepciones definidas en mi lista de configuración. Estas excepciones se corresponden con todas las acciones que están en el controlador <code>quest</code> y que por tanto tendrán unas URL&#8217;s asociadas del estilo <code>/quest/action/parameters</code>. Si no tuviera definidas estas restricciones para el primer mapeo, URL&#8217;s como <code>/quest/listAsPlayer/user1</code> terminarían invocando la acción <code>show</code> del controlador <code>quest</code>, estableciendo en <code>params.idText</code> el valor <code>listAsPlayer</code>.</p>
<h3>Algunos imprevistos</h3>
<p>Estas cosas no te las encuentras hasta que te pegas con ellas, pero si te puedo quitar un par de horas de curro mejor que mejor.</p>
<h4>Error al comprobar el mapeo a un vista</h4>
<p>Unos ejemplos típicos serían los siguientes:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="groovy" style="font-family:monospace;">assertUrlMapping<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">500</span>, view: <span style="color: #ff0000;">&quot;error&quot;</span><span style="color: #66cc66;">&#41;</span>
assertUrlMapping<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">404</span>, view: <span style="color: #ff0000;">&quot;404&quot;</span><span style="color: #66cc66;">&#41;</span></pre></td></tr></table></div>

<p>Lo que se pretende comprobar es que cuando se produce una respuesta HTTP con código 500 la vista que se muestra es <code>grails-app/views/error.gsp</code>. Lo mismo para el código 404 y su vista asociada.</p>
<p>En el momento de escribir este post <strong>existe un <em>bug</em> en Grails</strong> que impide el correcto funcionamiento: <a href="http://jira.codehaus.org/browse/GRAILS-4244" title="GrailsUrlMappingsTestCase broken for mappings to views">GRAILS-4244</a>. La <strong>solución rápida</strong> está en el enlace anterior: son necesarios dos <em>import</em> e incluir en el método <code>setUp</code> el siguiente código:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="groovy" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">import</span> <span style="color: #a1a100;">org.springframework.core.io.FileSystemResourceLoader</span>
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #a1a100;">org.springframework.core.io.support.PathMatchingResourcePatternResolver</span>
<span style="color: #000000; font-weight: bold;">class</span> UrlMappingTests <span style="color: #000000; font-weight: bold;">extends</span> grails.<span style="color: #006600;">test</span>.<span style="color: #006600;">GrailsUrlMappingsTestCase</span> <span style="color: #66cc66;">&#123;</span>
...
    <span style="color: #993333;">void</span> setUp<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> 
        <span style="color: #000000; font-weight: bold;">super</span>.<span style="color: #006600;">setUp</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> 
        patternResolver <span style="color: #66cc66;">=</span> <span style="color: #000000; font-weight: bold;">new</span> PathMatchingResourcePatternResolver<span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> FileSystemResourceLoader<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
    <span style="color: #66cc66;">&#125;</span>
...
<span style="color: #66cc66;">&#125;</span></pre></td></tr></table></div>

<h4>Comprobar realmente los parametros esperados en <code>params</code> en el proceso <code>ForwardUrlMapping</code></h4>
<p>Debido a otro <a href="http://jira.codehaus.org/browse/GRAILS-5222" title="Method assertForwardUrlMapping in GrailsUrlMappingsTestCase ignores the param assertions">bug en Grails</a>, el método <code>assertForwardUrlMapping</code> no comprueba realmente que que el proceso de mapeo deje en el objeto ímplicito <code>params</code> los valores esperados.</p>
<p>La solución pasaría por emplear nuestra propia implementación de la clase <code>grails.test.GrailsUnitTestCase</code>, heredar de ella y modificar el código tal como se recomienda en la página del propio bug.</p>
<h3>Terminando</h3>
<p>Bueno, me ha salido un poco largo, pero creo que está bien como complemento a la documentación que se puede encontrar por Internet. Espero que te sirva de inspiración para hacer las pruebas de tus mapeos, en realidad son las más sencillas de hacer y te puedes evitar muchas sorpresas.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/10/14/probando-url-mappings-en-grails/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Groovy Vs. Prolog Vs. Python</title>
		<link>http://blog.thuest.com/2009/10/11/groovy-vs-prolog-vs-python/</link>
		<comments>http://blog.thuest.com/2009/10/11/groovy-vs-prolog-vs-python/#comments</comments>
		<pubDate>Sun, 11 Oct 2009 14:52:39 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Desarrollo]]></category>

		<category><![CDATA[Groovy]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=23</guid>
		<description><![CDATA[Esto no va a ser una comparativa exhaustiva entre Groovy, Prolog y Python, simplemente unos snippets que intercambiamos mi colega fortran y yo hace un tiempo.
Contextualizemos: yo no sé Python, él no sabe Groovy, y probablemente el conocimiento que tiene él sobre Python es mayor que el que tengo yo sobre Groovy. Lo de Prolog [...]]]></description>
			<content:encoded><![CDATA[<p>Esto no va a ser una comparativa exhaustiva entre <a title="Groovy" href="http://groovy.codehaus.org">Groovy</a>, <a title="SWI-Prolog" href="http://www.swi-prolog.org/">Prolog</a> y <a title="Python" href="http://www.python.org">Python</a>, simplemente unos <em>snippets</em> que intercambiamos mi colega <a title="Blog de Fortran" href="http://fortran.blogspot.com">fortran</a> y yo hace un tiempo.</p>
<p>Contextualizemos: yo no sé Python, él no sabe Groovy, y probablemente el conocimiento que tiene él sobre Python es mayor que el que tengo yo sobre Groovy. Lo de Prolog tiene que ver con la universidad, el ansia por terminar prácticas y espinitas clavadas :-). Lo cierto es que hoy por hoy él controla mucho más que yo en programación lógica.</p>
<p>Quizás a alguien le resulte interesante, y en cualquier caso servirá en el futuro para recordar lo que éramos capaces de hacer (en ocasiones las mierdas del pasaso parecen ahora inalcanzables, no me lo negarás).</p>
<p><span id="more-23"></span></p>
<h3>Conseguir una lista de 100 número aleatorios, ordenarla e imprimirla</h3>
<p><strong>Python</strong></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">random</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">sorted</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">random</span>.<span style="color: black;">randint</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>,<span style="color: #ff4500;">2</span><span style="color: #66cc66;">**</span><span style="color: #ff4500;">31</span><span style="color: black;">&#41;</span><span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p><strong>Prolog</strong></p>

<div class="wp_syntax"><div class="code"><pre class="prolog" style="font-family:monospace;">r<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span>
r<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span><span style="color: #008080;">H</span><span style="color: #339933;">|</span><span style="color: #008080;">T</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:-</span>random<span style="color: #009900;">&#40;</span><span style="color: #800080;">0</span><span style="color: #339933;">,</span><span style="color: #800080;">1000000</span><span style="color: #339933;">,</span><span style="color: #008080;">H</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>r<span style="color: #009900;">&#40;</span><span style="color: #008080;">T</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span>
f<span style="color: #009900;">&#40;</span><span style="color: #008080;">L</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:-</span>length<span style="color: #009900;">&#40;</span><span style="color: #008080;">X</span><span style="color: #339933;">,</span><span style="color: #800080;">100</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>r<span style="color: #009900;">&#40;</span><span style="color: #008080;">X</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>sort<span style="color: #009900;">&#40;</span><span style="color: #008080;">X</span><span style="color: #339933;">,</span><span style="color: #008080;">L</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span></pre></div></div>

<p><strong>Groovy</strong></p>

<div class="wp_syntax"><div class="code"><pre class="groovy" style="font-family:monospace;">a<span style="color: #66cc66;">=</span><span style="color: #66cc66;">&#91;</span><span style="color: #66cc66;">&#93;</span>
100.<span style="color: #993399;">times</span><span style="color: #66cc66;">&#123;</span>a<span style="color: #66cc66;">&lt;&lt;</span><span style="color: #66cc66;">&#40;</span><span style="color: #993333;">int</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#40;</span><span style="color: #aaaadd; font-weight: bold;">Math</span>.<span style="color: #006600;">random</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">*</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">2</span><span style="color: #66cc66;">**</span><span style="color: #cc66cc;">31</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#125;</span>
<span style="color: #993399;">print</span> a.<span style="color: #663399;">sort</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<h3>Encontrar el indice de una sublista dentro de otra</h3>
<p><strong>Python</strong></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> f<span style="color: black;">&#40;</span>a,b<span style="color: black;">&#41;</span>:
 l=<span style="color: black;">&#91;</span>a<span style="color: black;">&#91;</span>i:i+<span style="color: #008000;">len</span><span style="color: black;">&#40;</span>b<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span>a<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
 <span style="color: #ff7700;font-weight:bold;">return</span> b <span style="color: #ff7700;font-weight:bold;">in</span> l <span style="color: #ff7700;font-weight:bold;">and</span> l.<span style="color: black;">index</span><span style="color: black;">&#40;</span>b<span style="color: black;">&#41;</span>or-<span style="color: #ff4500;">1</span></pre></div></div>

<p><strong>Prolog</strong></p>

<div class="wp_syntax"><div class="code"><pre class="prolog" style="font-family:monospace;">s<span style="color: #009900;">&#40;</span><span style="color: #008080;">X</span><span style="color: #339933;">,</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span>
s<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span><span style="color: #008080;">H</span><span style="color: #339933;">|</span><span style="color: #008080;">T</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span><span style="color: #009900;">&#91;</span><span style="color: #008080;">H</span><span style="color: #339933;">|</span><span style="color: #008080;">U</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:-</span>s<span style="color: #009900;">&#40;</span><span style="color: #008080;">T</span><span style="color: #339933;">,</span><span style="color: #008080;">U</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span>
f<span style="color: #009900;">&#40;</span><span style="color: #008080;">X</span><span style="color: #339933;">,</span><span style="color: #008080;">Y</span><span style="color: #339933;">,</span><span style="color: #800080;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:-</span>s<span style="color: #009900;">&#40;</span><span style="color: #008080;">X</span><span style="color: #339933;">,</span><span style="color: #008080;">Y</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span>
f<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span><span style="color: #008080;">_</span><span style="color: #339933;">|</span><span style="color: #008080;">T</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span><span style="color: #008080;">Y</span><span style="color: #339933;">,</span><span style="color: #008080;">N</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:-</span>f<span style="color: #009900;">&#40;</span><span style="color: #008080;">T</span><span style="color: #339933;">,</span><span style="color: #008080;">Y</span><span style="color: #339933;">,</span><span style="color: #008080;">M</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span><span style="color: #008080;">N</span> <span style="color: #990000;">is</span> <span style="color: #008080;">M</span><span style="color: #339933;">+</span><span style="color: #800080;">1.</span></pre></div></div>

<p><strong>Groovy</strong></p>

<div class="wp_syntax"><div class="code"><pre class="groovy" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">def</span> f<span style="color: #66cc66;">&#40;</span>a,b<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#123;</span><span style="color: #000000; font-weight: bold;">return</span> <span style="color: #aaaadd; font-weight: bold;">Collections</span>.<span style="color: #006600;">indexOfSubList</span><span style="color: #66cc66;">&#40;</span>a,b<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#125;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/10/11/groovy-vs-prolog-vs-python/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Lanzado Thuest 0.2</title>
		<link>http://blog.thuest.com/2009/09/30/lanzado-thuest-02/</link>
		<comments>http://blog.thuest.com/2009/09/30/lanzado-thuest-02/#comments</comments>
		<pubDate>Wed, 30 Sep 2009 18:50:48 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Funcionalidad]]></category>

		<category><![CDATA[Thuest]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=14</guid>
		<description><![CDATA[Después de tres semanas desde la anterior versión, he desplegado Thuest 0.2. Los objetivos fundamentales eran corregir errores y dotarle de alguna funcionalidad nueva que hicera más atractivo el proyecto.

Más tipos de respuestas
Conexión con twitter
Ranking de jugadores en cada quest
Nuevo criterio de búsqueda de quests: recompensas
Búsquedas de usuarios
Búsquedas en los foros

Continúa leyendo el post para [...]]]></description>
			<content:encoded><![CDATA[<p>Después de tres semanas desde la anterior versión, he desplegado <a title="Thuest" href="http://thuest.com">Thuest 0.2</a>. Los objetivos fundamentales eran corregir errores y dotarle de alguna funcionalidad nueva que hicera más atractivo el proyecto.</p>
<ul>
<li>Más tipos de respuestas</li>
<li>Conexión con twitter</li>
<li>Ranking de jugadores en cada <em>quest</em></li>
<li>Nuevo criterio de búsqueda de <em>quests</em>: recompensas</li>
<li>Búsquedas de usuarios</li>
<li>Búsquedas en los foros</li>
</ul>
<p>Continúa leyendo el post para ver los detalles de cada punto.</p>
<p><span id="more-14"></span></p>
<p><strong>Más tipos de respuestas</strong></p>
<p>Hasta ahora, en cada <em>checkpoint</em> sólo era posible introducir varias cadenas de texto simples como posibles respuestas válidas. Si el jugador introducía cualquiera de esas cadenas pasaría el <em>checkpoint</em>. En esta versión se han incluído tres opciones más:</p>
<ul>
<li>Texto multilínea: la respuesta correcta es un texto de varias líneas.</li>
<li>Una opción entre varias: al usuario se le presentan varias opciones y tiene que elegir la única que es correcta.</li>
<li>Una combinación de opciones entre varias: al usuario se le presentan varias opciones y tiene que seleccionar un conjunto de ellas.</li>
</ul>
<p><a href="http://blog.thuest.com/wp-uploads/2009/09/tiposrespuestasvariasopciones.png"><img class="aligncenter size-full wp-image-19" title="Checkpoint con respuesta multiple-choice" src="http://blog.thuest.com/wp-uploads/2009/09/tiposrespuestasvariasopciones.png" alt="Checkpoint con respuesta multiple-choice" /></a></p>
<p>La imagen muestra la edición de la respuesta de un <em>checkpoint</em>. Al usuario se le presentarán las cuatro opciones y tendrá que seleccionar Linus Torvalds y RMS para superar el <em>checkpoint</em>.</p>
<p><strong>Conexión con Twitter</strong></p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/09/conexiontwitter.png"><img class="aligncenter size-full wp-image-20" title="Conexión con twitter" src="http://blog.thuest.com/wp-uploads/2009/09/conexiontwitter.png" alt="Conexión con twitter" /></a></p>
<p>Desde la página del perfil del usuario es posible configurar una cuenta de <a title="Twitter" href="http://twitter.com">twitter</a> a la que enviar avisos automáticos sobre nuestra actuación en Thuest. Además no será necesario que nos proporciones tu nombre de usuario y password en twitter; gracias a <a title="OAuth" href="http://oauth.net">OAuth</a> en ningún momento tus credenciales pasarán por nuestras manos. ¡Mejor para todos!</p>
<p><strong>Ranking de jugadores en cada <em>quest</em></strong></p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/09/listajugadoresquest.png"><img class="aligncenter size-full wp-image-13" title="Lista de jugadores en un quest" src="http://blog.thuest.com/wp-uploads/2009/09/listajugadoresquest.png" alt="Lista de jugadores en un quest" width="482" height="175" /></a></p>
<p>Cualquier usuario, haya iniciado sesión o no, podrá acceder a la lista de jugadores de un <em>quest</em>. Podrá aplicar filtros para ver únicamente los jugadores que le interesen (jugando, quet abandonado o quest completado).</p>
<p><strong>Nuevo criterio de búsqueda de <em>quests</em>: recompensas</strong></p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/09/nuevocriteriobusqueda.png"><img class="aligncenter size-full wp-image-17" title="Nuevo criterio de búsqueda en quests" src="http://blog.thuest.com/wp-uploads/2009/09/nuevocriteriobusqueda.png" alt="Nuevo criterio de búsqueda en quests" /></a></p>
<p>Ahora, desde la página de <a title="Búsquedas avanzadas de quests en Thuest" href="http://thuest.com/search/searchQuest">búsquedas avanzadas</a>, es posible indicar si se desea encontrar únicamente <em>quests</em> con recompensas pendientes de ser asignadas. Especial para los cazatesoros <img src='http://blog.thuest.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><strong>Búsquedas de usuarios</strong></p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/09/buscadorusuarios.png"><img class="aligncenter size-full wp-image-18" title="Buscador de usuarios" src="http://blog.thuest.com/wp-uploads/2009/09/buscadorusuarios.png" alt="" width="466" height="137" /></a></p>
<p>Junto a los enlaces para buscar grupos de <em>quests</em> y clanes se ha añadido una nueva opción: la búsqueda de usuarios. Las búsquedas se realizarán en los emails, apodos y descripciones de los usuarios, aumentando las probabilidades de éxito.</p>
<p><strong>Búsquedas en los foros</strong></p>
<p>Desde la página de cada foro será posible buscar temas y respuestas, exclusivas del foro en cuestión, que coincidan con nuestra cadena de búsqueda. Desde la página de cada tema también será posible buscar respuestas, de nuevo exclusivas del foro en cuestión.</p>
<p><strong>Para ti&#8230;</strong></p>
<p>Internamente se han realizado algunos cambios que resultan indiferentes para la mayoría de los usuarios de la aplicación pero que te pueden resultar interesantes a ti, que has llegado hasta este punto del post.</p>
<ul>
<li>Se ha actualizado la versión de <a title="Grails" href="http://grails.org">Grails</a>, de la 1.0.4 a la 1.2-M2.</li>
<li>Se utiliza <a title="JMS" href="http://java.sun.com/products/jms/">JMS</a> para lanzar tareas asíncronamente, como el envío de emails.</li>
<li>Los <em>quests</em>, grupos de <em>quests</em>, usuarios y clanes ahora tienen una url de visualización más sencilla, en la que se utiliza un identificador generado a partir del nombre. Por ejemplo: <a title="Usuario chechu en Thuest" href="http://thuest.com/user/chechu">http://thuest.com/user/chechu</a>. Más intuitivo y fácil de recordar.</li>
</ul>
<p>Y para terminar, el avance en la definición y resolución de los <em>issues</em> asociados a esta versión. Para los morbosos.</p>
<p><a href="http://blog.thuest.com/wp-uploads/2009/09/progress.png"><img class="aligncenter size-full wp-image-21" title="Progreso del desarrollo en la versión 0.2" src="http://blog.thuest.com/wp-uploads/2009/09/progress.png" alt="Progreso del desarrollo en la versión 0.2" /></a></p>
<p>Me he fijado un ritmo de 15 días por versión, así es que espero cumplirlo y anunciaros Thuest 0.3 en un par de semanas.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/09/30/lanzado-thuest-02/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Plan de despliegue para thuest 0.2</title>
		<link>http://blog.thuest.com/2009/09/30/plan-de-despliegue-para-thuest-02/</link>
		<comments>http://blog.thuest.com/2009/09/30/plan-de-despliegue-para-thuest-02/#comments</comments>
		<pubDate>Wed, 30 Sep 2009 12:18:05 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Thuest]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=11</guid>
		<description><![CDATA[En la versión 0.2 de thuest se han introducido algunos cambios que han complicado un poco el despliegue. Recordemos que contamos con un servidor glassfish con un clúster que tiene asociada una instancia, sólo tenemos una máquina  
Los cambios en la nueva versión a los que me refería son básicamente la actualización a Grail [...]]]></description>
			<content:encoded><![CDATA[<p>En la versión 0.2 de thuest se han introducido algunos cambios que han complicado un poco el despliegue. Recordemos que contamos con un servidor glassfish con un clúster que tiene asociada una instancia, sólo tenemos una máquina <img src='http://blog.thuest.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Los cambios en la nueva versión a los que me refería son básicamente la actualización a Grail 1.2-M2, con su nueva gestión de dependencias transitivas con ivy (que te trae más mierda de la que necesitas) y el uso de JMS mediante <a title="Plugin JMS para Grails" href="http://grails.org/plugin/jms">el plugin de grails</a>.</p>
<p><span id="more-11"></span></p>
<p>Los <strong>pasos que sigo para desplegar</strong> son:</p>
<ol>
<li><code>chechu$&gt; grails clean</code>. Limpio.</li>
<li><code>chechu$&gt; grails get-dependencies</code>. Me traigo todas las dependencias configuradas con ivy.</li>
<li><code>chechu$&gt; sudo cp lib/xercesImpl-2.9.1.jar /opt/glassfish/lib; sudo cp lib/postgresql-8.3-603.jdbc4.jar /opt/glassfish/lib</code>. Estos JARS los necesita la aplicación, pero colocarlos en el PATH del servidor ha sido la mejor forma con la que he conseguido que todo funcione.</li>
<li><code>chechu$&gt; rm lib/xercesImpl-2.9.1.jar</code>. Dejar este JAR en la aplicación entraba en conflicto con el servidor y lo dejaba inestable totalmente.</li>
<li>Edito el fichero <code>.grails/1.2-M2/projects/theq/plugins/jms-0.5-RC2/src/groovy/grails/jms/listener/ListenerConfig.groovy</code> e indico que no quiero asociar a los <em>listener</em> JMS que se creen automáticamente un gestor de transacciones. Para ello eliminio la línea 125 (<em>transactionManager = ref(&#8221;transactionManager&#8221;)</em>).
<li><code>chechu$&gt; grails war</code>. Empaquetar.</li>
</ol>
<p>Por otro lado <strong>preparo el servidor glassfish</strong>:</p>
<ol>
<li>Elimino el despliegue anterior desde la consola web de glassfish.</li>
<li>Paro el dominio y el agente que gestiona la instancia</li>
<li><code>root$&gt; rm -rf /opt/glassfish/nodeagents/agent1/instance1/applications/j2ee-modules/thuest-*</code>. Elimino los restos de despliegues anteriores.</li>
<li><code>root$&gt; killall java</code>. Mato los procesos java que queden (con cuidado de que no tenga otras cosas de Java corriendo)</li>
<li><code>root$&gt; ps aux | grep java</code>. Me aseguro de que no queda nada corriendo de java, ni el <em>imqbrokerd</em>.
<li><code>root$&gt; asadmin start-node-agent agent1</code>. Arranco el agente que gestiona la instancia.
<li><code>root$&gt; asadmin start-domain thuest</code>. Arranco el dominio.
<li>Creo los recursos JDBC y JMS. Para la <em>connectionFactory</em> de JMS me aseguro de que la opción <code>Transaction Support</code> está a <code>NoTransaction</code>. También me aseguro de asignar el <em>target</em> correcto para todos los recursos.</li>
<li>Reinicio el clúster (lo paro y lo arranco), para que acceda a la nueva configuración. Para todo por completo si es necesario.</li>
<li>Despliego la aplicación</li>
</ol>
<p>Ficheros de <strong>logs interesantes</strong>:</p>
<ul>
<li><code>/opt/glassfish/nodeagents/agent1/instance1/logs/server.log</code>. El destino del log de la aplicación</li>
<li><code>/opt/glassfish/nodeagents/agent1/instance1/config/stacktrace.log</code>. Donde se vuelcan las excepciones de la instancia 1.</li>
<li>/opt/glassfish/nodeagents/agent1/instance1/imq/instances/myclusterinstance1/log/log.txt</code>. El log del <em>broker</em> de JMS arrancado por glassfish internamente.
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/09/30/plan-de-despliegue-para-thuest-02/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Grails + JMS + Clustering en glassfish</title>
		<link>http://blog.thuest.com/2009/09/30/grails-jms-clustering-en-glassfish/</link>
		<comments>http://blog.thuest.com/2009/09/30/grails-jms-clustering-en-glassfish/#comments</comments>
		<pubDate>Wed, 30 Sep 2009 12:14:36 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Desarrollo]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=10</guid>
		<description><![CDATA[Este post es una continuación de Grails + JMS + Email, donde se explicaba cómo configurar Grails 1.0.4 para que se enviaran emails mediante JMS. Una vez actualizado a Grails 1.2-M2 algunas de las cosas que comenté en su momento dejan de ser válidas.
Además se repasan los pasos básicos para definir un clúster en glassfish [...]]]></description>
			<content:encoded><![CDATA[<p>Este post es una continuación de <a title="Grails + JMS + Email" href="http://blog.thuest.com/2009/08/26/grails-jms-email/">Grails + JMS + Email</a>, donde se explicaba cómo configurar Grails 1.0.4 para que se enviaran emails mediante JMS. Una vez actualizado a Grails 1.2-M2 algunas de las cosas que comenté en su momento dejan de ser válidas.</p>
<p>Además se repasan los pasos básicos para definir un clúster en glassfish y se dan algunos consejos sobre aspectos a tener en cuenta y que pueden provocar errores dificilmente detectables.</p>
<p><span id="more-10"></span></p>
<p>Comencemos por las rectificaciones al post anterior:</p>
<ul>
<li>No es necesario añadir <code>returns</code> en el código del plugin, tal cual viene compila perfectamente.</li>
<li><strong>Deshabilitar el <code>transactionManager</code> se convierte en obligatorio al intentar desplegar la aplicación en un clúster de Glassfish</strong>. Al trabajar con un clúster de Glassfish las opciones para el <em>broker</em> JMS son tres: <code>EMBEDDED</code>, <code>LOCAL</code> y <code>REMOTE</code>.
<p>La opción <code>EMBEDDED</code> no tiene sentido (al menos yo no lo veo) si tenemos un clúster, ya que cada <em>broker</em> JMS se ejecutará en la JVM de cada instancia, perdiendo así la posibilidad de distribuir la carga que nos da JMS.</p>
<p>La opción <code>REMOTE</code> nos obliga a gestionar por nuestra cuenta un <em>broker</em> JMS mientras que con la opción <code>LOCAL</code> el propio servidor gestiona (arranca y para) un <em>broker</em>, concretamente ejecuta el binario <code>imqbrokerd</code> que viene en la propia distribución de glassfish.</p>
<p>Nuestra opción ha sido <code>LOCAL</code>, que es la opción por defecto al trabajar con clústers en glassfish. <a href="http://www.nabble.com/Re%3A-JMS-LOCAL-REMOTE-p18446635.html">Este mensaje</a> da la pista sobre un posible problema con las transacciones en la configuración <code>LOCAL</code> y recomienda deshabilitarlas. Así lo hice y funcionó perfectamente. Se deshabilita en la web de administración de Glassfish, al crear el <em>JMS connection factory</em> y también tenemos que eliminarlo en el plugin de JMS instalado en nuestra aplicación (fichero <code>~/.grails/1.2-M2/projects/<em>[proyecto]</em>/plugins/jms-0.5-RC2/src/groovy/grails/jms/listener/ListenerConfig.groovy</code>, línea 125 en la versión 0.5-RC2)</li>
<li>Sobre los receptores de mensajes, hay que asegurarse de que el valor devuelto sea serializable y tener en cuenta que en groovy si se declara el método como <code>def metodo...</code> se devolverá el valor de la última sentencia ejecutada en el método. Para evitar problemas, podemos fijar el método como <code>void</code>.</li>
</ul>
<p>Sobre los pasos a seguir <strong>para montar el clúster</strong>, podrían ser los siguientes:</p>
<ol>
<li><strong>Crear el dominio</strong>: <code>asadmin create-domain --profile cluster --portbase 5000 thuest</code>.</li>
<li><strong>Crear el <em>node-agent</em></strong> que gestionará la instancia: <code>asadmin create-node-agent --port 5048 agent1</code></li>
<li><strong>Crear el clúster</strong>: ya en la consola Web, Clusters -&gt; New. Indicamos un nombre y en la sección de instancias a ser creadas también seleccionamos New, asociando la nueva instancia al agente creado en el paso anterior. Al crear el clúster la instancia creada a la par será arrancada automáticamente. Cuando tengamos que aplicar nueva configuración es mejor parar y arrancar el clúster completo que cada instancia (de hecho al parar y arrancar una instancia individualemente te dará un error sobre operación no permitida).</li>
<li><strong>Aplicar configuración</strong>: parar y arrancar clúster (desde la consola web). Si deseamos parar y arrancar todo podemos usar la línea de comandos:
<ul>
<li><code>$&gt; asadmin stop-node-agent agent1</code>: para el agente <em>agent1</em></li>
<li><code>$&gt; asadmin stop-domain thuest</code>: para el dominio <em>thuest</em></li>
<li><code>$&gt; killall java</code>: a las bravas, mata todos los procesos java. Mucho cuidado porque esto afecta a procesos que a lo mejor no tienen nada que ver con glassfish</li>
</ul>
</li>
<li><strong>Balanceo</strong> con mod_jk. Ver <a title="Running GlassFish with Apache httpd" href="http://weblogs.java.net/blog/2006/03/17/running-glassfish-apache-httpd">este enlace</a> para hacerse una idea. Será necesario establecer una propiedad JVM, lo cual se hace a nivel de clúster: desde la consola Web ir a Configurations -&gt; mycluster-config -&gt; JVM Settings -&gt; JVM Options y añadir esto: <code>-Dcom.sun.enterprise.web.connector.enableJK=${WORKER_PORT}</code>. Ahora, para cada instancia habrá que definir una propiedad WORKER_PORT que es el puerto configurado en el mod_jk para un <em>worker</em>. Eso se hace desde la consola Web: Clusters -&gt; mycluster -&gt; instance1 -&gt; Properties. Una vez creada la propiedad reniciamos el clúster y comprobamos que podemos acceder a glassfish en el puerto 80 (que básicamente es lo que nos permite esto, además de poder conseguir un balanceo de carga si tienes varias instancias glassfish)</li>
<li><strong>Definir los recursos JMS y JDBC</strong>, teniendo cuidado con los nombres JNDI que les demos. Yo aconsejo no usar más de una / de indirección. Por ejemplo, <code>jms/connectionFactoryThuest</code> es preferible a <code>jms/connectionFactory/thuest</code>. Supongo que debe dar igual pero yo he sufrido algunos problemas inexplicables y seguir este consejo los solucionó. Además, asegurate de indicar el <code>target</code> correcto para cada recurso.</li>
<li><strong>Reinicia</strong> el clúster, el agente y el dominio todas las veces que hagan falta hasta asegurarte de que los recursos se ven por quien tienen que ser vistos. Mata procesos, elimina restos de despliegues anteriores y <strong>no te canses de empezar desde cero</strong> una y otra vez.</li>
</ol>
<p>Son muchas pequeñas cosas, a lo mejor a ti no te afecta todo, pero nunca sobran cosas que repasar cuando algo va mal. Más adelante publicaré mi plan de despliegue de la aplicación, para que yo me acuerde y sirva de ayuda a quien la necesite.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/09/30/grails-jms-clustering-en-glassfish/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Grails + JMS + Email</title>
		<link>http://blog.thuest.com/2009/08/26/grails-jms-email/</link>
		<comments>http://blog.thuest.com/2009/08/26/grails-jms-email/#comments</comments>
		<pubDate>Wed, 26 Aug 2009 18:52:32 +0000</pubDate>
		<dc:creator>chechu</dc:creator>
		
		<category><![CDATA[Desarrollo]]></category>

		<guid isPermaLink="false">http://blog.thuest.com/?p=5</guid>
		<description><![CDATA[La situación es la siguiente: un usuario origina la ejecución de una acción en el servidor, la cual debe realizar una o varias tareas que pueden requerir una gran cantidad de tiempo y cuyo estado final no influye en la respuesta que se envíe al usuario. Nuestro caso concreto: envío de emails como consecuencia de [...]]]></description>
			<content:encoded><![CDATA[<p>La situación es la siguiente: un usuario origina la ejecución de una acción en el servidor, la cual debe realizar una o varias tareas que pueden requerir una gran cantidad de tiempo y cuyo estado final no influye en la respuesta que se envíe al usuario. Nuestro caso concreto: envío de emails como consecuencia de una acción solicitada por un usuario.</p>
<p>En <a title="Thuest" href="http://thuest.com">Thuest</a> esta situación se repite bastantes veces. Por ejemplo, cuando un usuario se hace fan de otro se le envía un correo a este último informando. Un primer desarrollo de la acción <code>convertirseEnFan</code> podría ser:</p>
<pre>1º Recuperar de la BBDD el nuevo ídolo
2º Crear y guardar una nueva instancia Fan, que relacione al usuario de sesión y al ídolo recuperado
3º <strong>Mandar un email al ídolo</strong>
4º Renderizar la respuesta al usuario</pre>
<p>El paso que nos interesa es el tercero. Si ese envío se realiza de forma síncrona la acción completa sufrirá una penalización bastante grande respecto al tiempo necesario. La comunicación con el servidor SMTP puede retrasarse y si a esto le sumamos que podemos tener acciones que conlleven el envío de muchos emails, realizar esta operación de forma asíncrona resulta muy interesante.</p>
<p>Podríamos tirar por el camio de en medio y crearnos <em>threads</em> para que hicieran el trabajo, incluso podríamos usar un <em>pool</em> de <em>threads</em> si queremos ser más elegantes. Pero en Java tenemos <a title="Java Message Service" href="http://java.sun.com/products/jms">JMS</a>, que a mi es lo primero que se me viene a la cabeza en estos casos. Además, una vez configurado JMS verás como se te ocurren muchas aplicaciones (el tema los <em>topics</em> da mucho juego y puedes montar un sistema de notificaciones bastante chulo).</p>
<p><span id="more-5"></span></p>
<h3>Plugin JMS para Grails</h3>
<p>Vamos a centrarnos. Puesto que estamos trabajando con <a title="Grails" href="http://grails.org">Grails</a>, nuestro objetivo es hacer funcionar JMS en este <em>framework</em> y poder enviar emails de forma asíncrona. Para lo primero comenzamos por instalar el plugin correspondiente:</p>
<pre>$&gt; grails install-plugin jms</pre>
<p>En la <a title="Plugin JMS para Grails" href="http://grails.org/plugin/jms">página del plugin</a> hay información más detallada de lo que yo pueda explicar aquí, échale un ojo. Particularmente yo tuve que modificar el código del plugin (versión 0.5-RC2), incluyendo <em>returns</em> que el programador entendió opcionales pero que no lo eran tanto. Los ficheros afectados fueron <code>JmsService.groovy</code>, <code>ListenerConfig.groovy</code> y <code>ServiceInspector.groovy</code>.</p>
<p>Además, en el fichero <code>ListenerConfig.groovy</code> se definen los <em>listener</em> que realmente estarán esperando los nuevos mensajes. El método <code>registerListenerContainer</code> tiene el <em>builder</em> que construye estos objetos. En ese <em>builder</em> es posible que te interese deshabilitar el uso de un gestor de transacciones; para ello comenta la linea donde se asigna valor al <code>transactionManager</code>.</p>
<p>El último paso es proporcionar al contexto de ejecución de la aplicación un <em>bean</em> capaz de crear conexiones con nuestro <em>broker</em> de JMS. Este<em> bean</em> será usado por el plugin para establecer las conexiones. Podemos usar el fichero <code>grails-app/conf/spring/resources.groovy</code> para realizar la definición de este <em>bean</em>. Un ejemplo vale más que mil palabras, así es que aquí está el contenido de mi <code>resources.groovy</code></p>
<pre>import grails.util.GrailsUtil
if(GrailsUtil.environment == "production") {
  beans = {
    jmsConnectionFactory(org.springframework.jndi.JndiObjectFactoryBean) {
      jndiName = 'jms/connectionFactory/thuest'
    }
  }
} else if(GrailsUtil.environment == "development") {
  beans = {
    jmsConnectionFactory(com.sun.messaging.ConnectionFactory) {
      // Default values
    }
  }
}</pre>
<p>Por defecto, el plugin de JMS espera un <em>bean</em> con nombre <code>jmsConnectionFactory</code>, y así vamos a dejarlo. En función del entorno de ejecución en el que estemos definiremos el <em>bean</em> <code>jmsConnectionFactory</code> de una forma o de otra. Si estamos en producción le indicamos que recupere el <em>bean</em> mediante jndi y la clase porporcionada por Spring JndiObjectFactoryBean (esto acarrea configuración en el servidor, más adelante la veremos). Si estamos en desarrollo indicamos que la clase del <em>bean</em> es <code>com.sun.messaging.ConnectionFactory</code> (para el caso particular de <a title="OpenMQ" href="http://mq.dev.java.net/">OpenMQ</a>, para <a title="ActiveMQ" href="http://activemq.apache.org">ActiveMQ</a> ver la página del <a title="Plugin JMS para Grails" href="http://grails.org/plugin/jms">plugin JMS</a>) y aceptamos los valores por defecto. Esto valores por defecto implican tener corriendo una instancia del <code>imqbrokerd</code> en el puerto 7676. <code>imqbrokerd</code> viene con <a title="Glassfish" href="http://glassfish.dev.java.net">Glassfish</a> (mira en el directorio imq/bin de la instalación de Glassfish).</p>
<p>El plugin está listo, siguiente paso.</p>
<h3>Receptores de mensajes</h3>
<p>Vamos a aislar la funcionalidad que queremos ejecutar asícronamente en un servicio, en mi caso MessageService. En ese servicio hay que incluir el siguiente atributo:</p>
<pre>class MessageService {
  ...
  static exposes = ['jms']
  &#8230;
}</pre>
<p>Este atributo es el que busca el plugin internamente para saber si un servicio tiene métodos asíncronos.</p>
<p>Para definir los métodos asíncronos en estre caso creo que la forma más precisa es usar las anotaciones <code>@Queue</code> o <code>@Topic</code>, en función de lo que queramos. Yo por ejemplo tengo lo siguiente:</p>
<pre>import grails.jms.Queue
class MessageService {
  ...
  static exposes = ['jms']
  @Queue
  def sendEmailMessage(EmailMessagePack msg) {&#8230;}
}</pre>
<h3>Serializando que es gerundio</h3>
<p>El plugin de JMS inyecta en todos los servicios y controladores el método <code>sendJMSMessage</code> (entre otros), que en su forma más básica acepta un destinatario y un mensaje. El destinatario se expresa como un mapa, dos ejemplos:</p>
<pre>  sendJMSMessage([queue:'thuest.message.sendEmailMessage'], mensaje)
  sendJMSMessage([service:'message', method:'sendEmailMessage'], mensaje)</pre>
<p>Todo objeto que se envía como mensaje debe ser serializable y sus atributo también para que la recursividad que se aplica por defecto no dé errores. Si tienes que enviar varios contenidos créate una clase para la ocasión (recuerda que debe implementar Serializable) y define los distintos contenidos como atributos de la nueva clase. La alternativa podría ser crearte un <em>converter</em> de mensajes, pero yo he optado por la primera opción, lo veía más sencillo.</p>
<p>Si se envía como mensaje un mapa los valores sólo pueden ser tipos básicos de Java y String.</p>
<h3>Configuración en glassfish</h3>
<p>Lo primero es definir el objeto <code>ConnectionFactory</code> accesible por jndi. Para ello, empleando la interfaz web de administración de Glassfish, vamos a Resources-&gt;JMS Resources-&gt;Connection Factories-&gt;New. El nombre jndi será el mismo que especificamos en el <code>resources.groovy</code> (en nuestro caso <code>jms/connectionFactory/thuest</code>) y como tipo de recurso yo elegí <code>javax.jms.QueueConnectionFactory</code> al trabajar con <em>queues</em>, si trabajas con <em>topics</em> elige la otra opción.</p>
<p>Llega el turno de definir los destinos de los mensajes, en nuestro caso las <em>queues</em>. Por defecto para el plugin de JMS los nombres jndi de las colas asociadas a mensajes tienen el formato <code>nombreAplicacion.nombreServicio.nombreMetodo</code>, por ejemplo <code>thuest.message.sendEmailMessage</code>. Por lo tanto la configuración en Glassfish supondrá crear tantas colas como métodos asíncronos hayamos definido con el plugin de JMS, usando los nombres correspondientes. Esto lo podemos hacer desde la interfaz web de administración de Glassfish: Resources-&gt;JMS Resources-&gt;Destination Resources-&gt;New.</p>
<p>Los <em>physical destinations</em> solicitados para crear las <em>queues</em> se crean en distintos lugares en función de si estamos trabajando con <em>clusters</em> o no. Si estamos trabajando con <em>clusters</em>, en la interfaz web de administración de Glassfish, ve a Clusters-&gt;Mi Cluster-&gt;Physical Destinations-&gt;New. Si no trabajas con <em>clusters</em> ve a Stand-Alone instances-&gt;Mi server-&gt;Physical Destinations-&gt;New.</p>
<h3>Envío de emails</h3>
<p>El primer paso es instalar el <a title="Plugin mail para Grails" href="http://www.grails.org/plugin/mail">plugin de mail de Grails</a>:</p>
<pre>  $&gt; grails install-plugin mail</pre>
<p>Me saltaré el paso de la configuación y uso porque en la página del plugin viene muy bien explicado. En relación al tema que estamos tratando, aconsejaría que el envío de emails se aisle en un método único, así se podría aislar y hacer asíncrono más fácilmente.</p>
<p>Si vas a usar <em>templates</em> gsp como cuerpos de los correos (algo bastante potente y recomendable) ten en cuenta que el envío se realizará fuera de un contexto <em>request</em>. ¿Y qué?. Pues que internamente el objeto implícito <em>request</em> (que no existirá en este caso) puede ser usado en distintos sitios, por ejemplo por el <em>tag</em> <code>g:applyLayout</code>, así es que si pretendes hacer <em>layouts</em> en tus emails vete olvidando.</p>
<h3>Y fin</h3>
<p>Creo haber hablado de lo más importante, si tienes alguna duda o piensas que hay algo erróneo en lo que he escrito, usa los comentarios, que aquí estamos para aprender.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thuest.com/2009/08/26/grails-jms-email/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
