<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10spanishfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;Dk4MQX8-fip7ImA9WhFSFEo.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900</id><updated>2013-06-17T15:16:20.156+02:00</updated><category term="juegos" /><category term="GDM" /><category term="Minix" /><category term="Microsoft" /><category term="hola" /><category term="javascript" /><category term="personalización" /><category term="software libre" /><category term="java" /><category term="desempaquetado" /><category term="software privativo" /><category term="wallpaper" /><category term="GNU/Linux" /><category term="raspberrypi" /><category term="GRUB2" /><category term="bash" /><category term="Apple" /><category term="VLC" /><category term="gnome" /><category term="PHP" /><category term="otros" /><category term="android" /><category term="programación" /><category term="blogger" /><category term="arch linux" /><category term="software" /><category term="hemeroteca" /><category term="planeta código" /><category term="planeta arch linux" /><category term="tecnología" /><category term="ubuntu" /><category term="hardware" /><category term="google" /><category term="planeta linux" /><category term="tapestry" /><title>El blog de pico.dev</title><subtitle type="html">Blog dedicado a la distribución GNU/Linux que uso más habitualmente, Arch Linux, a mis andanzas entorno al software libre, la programación y a otros temas relacionados con la tecnología y la informática.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://elblogdepicodev.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>pico.dev</name><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>162</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/blogspot/HkNj" /><feedburner:info uri="blogspot/hknj" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="license" type="text/html" href="http://creativecommons.org/licenses/by-nc/3.0/" /><feedburner:emailServiceId>blogspot/HkNj</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare href="http://add.my.yahoo.com/content?lg=es&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://eur.i1.yimg.com/eur.yimg.com/i/es/my/addto1.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.feedness.com/alta/http://feeds.feedburner.com/blogspot/HkNj" src="http://www.feedness.com/ayuda/wp-content/square_b_sh_feed.gif">Subscribe with Feedness</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/blogspot/HkNj" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsalloy.com/?rss=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.newsalloy.com/subrss3.gif">Subscribe with NewsAlloy</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://download.attensa.com/app/get_attensa.html?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.attensa.com/blogs/attensa/WindowsLiveWriter/BadgeredintoBadges_10C02/attensa_feed_button5.gif">Subscribe with Attensa for Outlook</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.flurry.com/pushRssFeed.do?r=fb&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.flurry.com/images/flurry_rss_logo2.gif">Subscribe with Flurry</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FHkNj" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><entry gd:etag="W/&quot;DE4GQnc9eCp7ImA9WhFSEk8.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-3558188407207111284</id><published>2013-06-14T18:10:00.000+02:00</published><updated>2013-06-14T18:22:03.960+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-06-14T18:22:03.960+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Mantenimiento CRUD en Apache Tapestry</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apache Tapestry" border="0" height="133" src="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s320/tapestry.png" title="Apache Tapestry" width="320" /&gt;&lt;/div&gt;
El scaffolding es una ayuda que permite a partir de una descripción disponer de una funcionalidad sin escribir una línea de código o muy pocas exceptuando las necesarias para la descripción. En funcionalidades que se repiten mucho en una aplicación, no aportan mucho valor y son aburridas de programar puede llegar ahorrar una cantidad considerable de tiempo y disponer de un prototipo funcional muy rápidamente. Es utilizada por varios frameworks web en diferentes lenguajes para hacer los mantenimientos CRUD (Create, Read, Update, Delete) de las tablas de una base de datos relacional:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Ruby on Rails&lt;/li&gt;
&lt;li&gt;Symfony&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;Y quizá algunos otros menos conocidos&lt;/li&gt;
&lt;/ul&gt;
El problema de los scaffoldings viene cuando la funcionalidad que proporcionan no es suficiente, no se adapta a lo que necesitamos o no nos gusta lo que se genera y nos vemos en la obligación de personalizarlo, los scaffolding son tan básicos que si tenemos una aplicación un poco compleja probablemente deberemos adaptarlos. En ese caso no nos quedará más remedio que programar lo que necesitemos. Algunos frameworks permiten generar un código inicial a partir del que comenzar la programación. Esto es el caso de Grails y el código generado deja bastante que desear (en mi humilde opinión), el CRUD de una tabla tiene cinco gsp, entre ellas una para el alta y otra para la modificación que son prácticamente iguales con lo que contiene bastante código repetido también en el controlador para las acciones save y update que para luego mantenerlo supone problemas. Una vez que el código está generado y lo hemos personalizado con alguna modificación al salir una nueva versión del framework (y tarde o temprano saldrá) tendremos la duda de si el código que se nos generó con una versión anterior se ha quedado obsoleto en la nueva versión, dudaremos si partir del código inicial que genera la nueva versión e incorporar las cambios que hicimos o si adaptamos el código que tenemos con los nuevos cambios.&lt;br /&gt;
&lt;br /&gt;
Como resultado al final disponer de un framework que tenga scaffolding con el tiempo puede no suponer tanto ahorro de tiempo como puede parecer en un principio. Tapestry no proporciona un scaffolding para hacer estos CRUD aunque si proporciona varios componentes avanzados que ayudan mucho a realizarlos (&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Grid.html"&gt;Grid&lt;/a&gt;, &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/BeanEditor.html"&gt;BeanEditor&lt;/a&gt;). A continuación pondré un ejemplo de una funcionalidad similar a un CRUD realizada con el framework Apache Tapestry. En el ejemplo se podrá mantener una tabla de una base de datos pudiendo dar de alta una nuevos registros, eliminarlos, modificarlos y ver sus datos en una tabla con paginación Ajax. En el se verá que todo esto no supone más de 200 líneas de código Java y 80 líneas de código de presentación en tml. Necesitará solo dos archivos, uno para código Java, otro para el de presentación tml e increiblemente ninguno para código Javascript a pesar de hacer la paginación via Ajax. En estos números no estoy incluyendo varias clases de apoyo como la entidad a persistir o su DAO (Data Access Object) ya que no son propios de un único scaffolding sino que se podría utilizar para todos los que tengamos en la aplicación.&lt;br /&gt;
&lt;br /&gt;
En la entrada sobre &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/06/persistencia-con-jpa-y-apache-tapestry.html"&gt;persistencia JPA usando Tapestry&lt;/a&gt; ya expliqué como construir el servicio y la transaccionalidad. En el siguiente código utilizaré ese servicio DAO para el acceso a la base de datos, no cambia nada. Me centraré en poner el código necesario para la página que necesita proporcionar la funcionalidad del scaffolding.&lt;br /&gt;
&lt;br /&gt;
A continuación el código Java en la que principalmente Tapestry se encarga de llamar al método que actúa de escuchador para cada evento que se produzca en la página, en función del evento se llamará a la operación DAO adecuada y actualizar el estado del controlador.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5782703.js?file=ProductoAdmin.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5782703/raw/ProductoAdmin.java"&gt;ProductoAdmin.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Ahora el archivo tml que se encarga de generar el html que se enviará al navegador. En el mismo archivo está el código para mostrar el listado y para realizar la edición tanto en el alta como en la modificación. En función del estado de la página se mostrará uno u otro haciendo uso del &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Delegate.html"&gt;componente Delegate&lt;/a&gt;. Para ver la paginación en funcionamiento sin necesidad de crear muchos objetos solo será necesario crear dos, esto es, las páginas solo contendrán dos elementos.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5782703.js?file=ProductoAdmin.tml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5782703/raw/ProductoAdmin.tml"&gt;ProductoAdmin.tml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Eso es todo lo propio que necesita el scaffolding y sería lo único necesario para realizar otros similares. Sin embargo, este hace uso de unas cuantas clases de utilidad. Entre ellas la clase que proporciona la funcionalidad de paginación al acceder a la lista de entidades. A partir de los datos de paginación que el data source indica (elemento de inicio, de fin y orden) se construye el objeto que contendrá esta información y se le enviará al DAO para que haga la consulta adecuada.&lt;br /&gt;
&lt;br /&gt;
Finalmente, como comenté un una &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/05/seguridad-en-aplicacion-web-con-apache-II.html"&gt;entrada anterior sobre seguridad sobre XSS&lt;/a&gt; (Cross Site Scripting) el mantenimiento está protegido ante este tipo de ataques (puedes probar crear un producto con &amp;lt;script&amp;gt;alert(1);&amp;lt;/script&amp;gt;), para ello no hemos hecho nada especial.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5782703.js?file=JPAGridDataSource.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5782703/raw/JPAGridDataSource.java"&gt;JPAGridDataSource.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Y otro par de clases muy sencillitas que continenen los datos para que el DAO sepa como se quiere la ordenación y que elementos paginados se quieren.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5782703.js?file=Pagination.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5782703/raw/Pagination.java"&gt;Pagination.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5782703.js?file=Sort.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5782703/raw/Sort.java"&gt;Sort.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5782703.js?file=JPAGridDataSource.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5782703/raw/JPAGridDataSource.java"&gt;JPAGridDataSource.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

El resultado de la lista, la modificación de un producto y el alta habiendo errores de validación es este:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-_ACF7tvPFU0/UbtCTp7zCkI/AAAAAAAACjY/KkI_ykDcqt8/s1600/tapestry-crud-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://3.bp.blogspot.com/-_ACF7tvPFU0/UbtCTp7zCkI/AAAAAAAACjY/KkI_ykDcqt8/s200/tapestry-crud-1.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-TukV1EqPfl8/UbtCThoddAI/AAAAAAAACjQ/9yyw2vYCdDs/s1600/tapestry-crud-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-TukV1EqPfl8/UbtCThoddAI/AAAAAAAACjQ/9yyw2vYCdDs/s200/tapestry-crud-2.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-nmeMujOANiw/UbtCTgCFngI/AAAAAAAACjU/-ZV20u6dEqY/s1600/tapestry-crud-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="176" src="http://3.bp.blogspot.com/-nmeMujOANiw/UbtCTgCFngI/AAAAAAAACjU/-ZV20u6dEqY/s200/tapestry-crud-3.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Como en el resto de entradas el &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestryJPA"&gt;código fuente completo&lt;/a&gt; lo puedes encontrar en &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master"&gt;mi repositorio de GitHub&lt;/a&gt;. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes &lt;a href="https://github.com/picodotdev/elblogdepicodev/archive/master.zip"&gt;obtener el código fuente del repositorio en un archivo zip&lt;/a&gt; con el anterior enlace.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5782703.js?file=git.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5782703/raw/git.sh"&gt;git.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/iwLCMzaxq3c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/3558188407207111284/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/06/mantenimiento-crud-en-apache-tapestry.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3558188407207111284?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3558188407207111284?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/iwLCMzaxq3c/mantenimiento-crud-en-apache-tapestry.html" title="Mantenimiento CRUD en Apache Tapestry" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s72-c/tapestry.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/06/mantenimiento-crud-en-apache-tapestry.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMNR3w4eCp7ImA9WhFTFk4.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1773578262526819275</id><published>2013-06-07T20:24:00.000+02:00</published><updated>2013-06-07T21:48:16.230+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-06-07T21:48:16.230+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Persistencia con JPA y Apache Tapestry</title><content type="html">En Java disponemos de varias opciones para persistir la información a una base de datos relacional, algunas de ellas son:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apache Tapestry" border="0" height="133" src="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s320/tapestry.png" title="Apache Tapestry" width="320" /&gt;&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Hibernate" border="0" src="http://4.bp.blogspot.com/-NW8tZ9bOLc0/Tr1-LwGV1KI/AAAAAAAAA_E/Ix83vOC01Nk/s1600/hibernate.png" title="Hibernate" /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;JDBC: es la API que viene integrada en la propia plataforma Java sin necesidad de ninguna librería adicional exceptuando el driver JDBC para acceder a la base de datos. Mediante esta opción se tiene total flexibilidad y evita la abstracción y sobrecarga de los sistemas como Hibernate y JPA. Se trabaja con el lenguaje SQL de forma directa y este lenguaje puede varia en algunos aspectos de una base de datos a otra con lo que para migrar a otra base de datos puede implicar reescribir las SQL de la aplicación. Su utilización de forma directa no es tan habitual aunque en casos que se necesite acceder de forma masiva a los datos puede se útil para evitar la sobrecarga o complejidad que añaden sistemas como Hibernate o JPA.&lt;/li&gt;
&lt;li&gt;Hibernate: el modelo relacional de las bases de datos es distinto del modelo de objetos del los lenguajes orientados a objetos. Los sistemas ORM como Hibernate tratan de hacer converger el sistema relacional hacia un modelo más similar al modelo de objetos de lenguajes como Java, de forma que trabajar con ellos sea como trabajar con objetos. En ORM como Hibernate normalmente no se trabaja a nivel de SQL como con JDBC sino que se trabaja con objetos (POJO), las consultas devuelven objetos, las relaciones se acceden a través de propiedades y los borrados, actualizaciones y inserciones se realizan usando objetos y métodos. Los objetos POJO incluyen anotaciones que le indican a Hibernate cual es la información a persistir y las relaciones con otros POJO. Como hibernate dispone de esta información en base a ella puede recrear o actualizar las tablas y los campos necesarios según la definición de esas anotaciones. El ORM es encarga de traducir las acciones a las SQL entendidas por el sistema relacional, esto proporciona la ventaja adicional de que el ORM puede generar las sentencias SQL adaptadas a la base de datos utilizada. De esta forma se podría cambiar de una base de datos a otra sin realizar ningún cambio en la aplicación o con pocos cambios comparado con los necesarios usando JDBC. Con Hibernate se puede emplear un lenguaje de consulta similar a SQL pero adaptado al modelo orientado a objetos, el lenguaje es HQL.&lt;/li&gt;
&lt;li&gt;JPA: es una especificación de Java que define una API común para los sistemas ORM. Con JPA podríamos cambiar de proveedor ORM sin realizar ningún cambio en la aplicación. JPA se ha basado en gran parte en Hibernate y su forma de trabajar es similar, el lenguaje HQL también es similar pero denominado JPQL.&lt;/li&gt;
&lt;/ul&gt;
A continuación explicaré como acceder a una base de datos relacional a través de JPA en una aplicación web usando el framework &lt;a href="http://tapestry.apache.org/"&gt;Apache Tapestry&lt;/a&gt;.

Lo primero que deberemos hacer es crear el archivo persistence.xml donde indicaremos el driver JDBC según la base de datos que utilicemos y la URL de conexión a la base de datos entre otras opciones como el usuario y password de conexión. En el ejemplo he usado H2 como base de datos ya que puede embeberse en una aplicación sin necesidad de tener un sistema externo como ocurren en el caso de MySQL y PostgreSQL. De esta forma este ejemplo puede probarse sin necesidad de instalar previamente ninguna base de datos relacional.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=persistence.xml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/persistence.xml"&gt;persistence.xml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En la aplicación es necesario incluir unas pocas dependencias para acceder a la API JPA.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=build.gradle"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/build.gradle"&gt;build.gradle&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Una vez incluidas las dependencias debemos configurar Tapestry para que nos proporcione el soporte de acceso a una base de datos empleando JPA, definimos en el módulo de la aplicación y contenedor de dependencias, el servicio DAO y al mismo tiempo configuraremos la transaccionalidad mediante anotaciones para el DAO.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=AppModule.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/AppModule.java"&gt;AppModule.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Las clases con capacidad de persistencia han de ubicarse en un subpaquete del paquete de Tapestry. El paquete de Tapestry es aquel que está indicado en el parámetro de contexto tapestry.app-package en el archivo web.xml de la aplicación web. Si tapestry.app-package fuese es.com.blogspot.elblogdepicodev.tapestry.jpa el paquete de las entidades debería ser es.com.blogspot.elblogdepicodev.tapestry.jpa.entities. Esta es la convención y la forma preferida de hacerlo, si se quiere cambiar es posible hacerlo mediante configuración. La entidad de persistencia que usaré para el ejemplo es la siguiente, en la que se incluyen las anotaciones de JPA:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=Producto.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/Producto.java"&gt;Producto.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

El código de acceso a base de datos suele ponerse en una clase denominada servicio y &lt;a href="http://en.wikipedia.org/wiki/Data_access_object"&gt;DAO&lt;/a&gt; (Data Access Object) que contiene todo ese código. Ya que las operaciones de acceso a base de datos son candidatas a ser reutilizadas desde varias páginas o componentes es recomendable hacerlo así, además de hacer que las páginas de Tapestry sean más pequeñas (ya tienen suficiente responsabilidad con hacer de controlador en el modelo MVC) permite que si un día cambiásemos de framework web solo tendríamos que modificar la capa de presentación. Todo el código de los servicios nos serviría perfectamente sin hacer ninguna modificación.&lt;br /&gt;
&lt;br /&gt;
El contenedor de dependencias se encargará en el momento que necesite construir una instancia del servicio DAO inyectarle en el constructor los parámetros necesarios. En este caso una de las clases principales de la API JPA que es &lt;a href="http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html"&gt;EntityManager&lt;/a&gt;. Una vez con la referencia a entityManager trabajaríamos usando sus métodos para realizar las consultas y operaciones que necesite proporcionar el DAO.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=ProductoDAOImpl.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/ProductoDAOImpl.java"&gt;ProductoDAOImpl.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Hay métodos del DAO que finalmente lanzan sentencias de inserción, modificación y borrado, este DAO es muy sencillo pero si realizase varias operaciones contra la base de datos estas deberían cumplir las reglas ACID. Para cumplir las reglas ACID hay que utilizar transacciones, se puede conseguir de forma declarativa utilizando la anotación &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/hibernate/annotations/CommitAfter.html"&gt;@CommitAfter&lt;/a&gt;. Esta anotación inicia una transacción al iniciarse el método y lanza un commit al finalizar el método. En caso de que se produzca una excepción se seguirá haciendo commit para las excepciones checked (de obligado tratamiento o lanzado) y únicamente un rollback para las excepciones unckecked (que extienden de RuntimeException y no es necesario capturarlas o lanzarlas).&lt;br /&gt;
&lt;br /&gt;
Si quisiésemos hacer rollback en una excepción checked deberíamos hacerlo manualmente capturando la excepción y lanzando una unchecked aunque si es la opción que queremos por defecto podríamos implementar una anotación para tratar la transaccionalidad de esta manera y de forma declarativa en todos los casos.&lt;br /&gt;
&lt;br /&gt;
Una vez que tenemos el servicio DAO basta con inyectarlo en la clase de una página o componente y hacer uso de él. En la siguiente página se muestra un listado de productos donde con un botón se pueden eliminar y con otro se pueden crear nuevos. En la clase de la página se hace uso del DAO para buscar las entidades, crear una nueva con un nombre y descripción aleatoria y eliminar un producto.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=EjemploJPA.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/EjemploJPA.java"&gt;EjemploJPA.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=EjemploJPA.tml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/EjemploJPA.tml"&gt;EjemploJPA.tml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Esta es una captura de pantalla del ejemplo en ejecución:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-l3egS-am0hc/UbIauyheMQI/AAAAAAAACig/zTaynsNbRyk/s1600/tapestry-ejemplo-jpa.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="137" src="http://4.bp.blogspot.com/-l3egS-am0hc/UbIauyheMQI/AAAAAAAACig/zTaynsNbRyk/s200/tapestry-ejemplo-jpa.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
Para casos más complejos de transacciones como transacciones anidadas, requeridas, soportables, ninguna u obligatoria podemos basarnos en lo comentado en &lt;a href="http://tawus.wordpress.com/2011/04/23/tapestry-magic-5-advising-services/"&gt;esta entrada&lt;/a&gt; donde queda perfectamente explicado.&lt;br /&gt;
&lt;br /&gt;
Como en el resto de entradas el &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestryJPA"&gt;código fuente completo&lt;/a&gt; lo puedes encontrar en &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master"&gt;mi repositorio de GitHub&lt;/a&gt;. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el &lt;a href="https://github.com/picodotdev/elblogdepicodev/archive/master.zip"&gt;código fuente del repositorio en un archivo zip&lt;/a&gt; con el anterior enlace.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5731017.js?file=git.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5731017/raw/git.sh"&gt;git.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://es.wikipedia.org/wiki/Java_Database_Connectivity"&gt;http://es.wikipedia.org/wiki/Java_Database_Connectivity&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://en.wikipedia.org/wiki/Relational_database"&gt;https://en.wikipedia.org/wiki/Relational_database&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://en.wikipedia.org/wiki/Cardinality_(data_modeling)"&gt;http://en.wikipedia.org/wiki/Cardinality_(data_modeling)&lt;/a&gt;
&lt;a href="http://en.wikipedia.org/wiki/ACIDhttp://en.wikipedia.org/wiki/SQL"&gt;http://en.wikipedia.org/wiki/ACIDhttp://en.wikipedia.org/wiki/SQL&lt;/a&gt;
&lt;a href="http://en.wikipedia.org/wiki/NoSQLhttp://en.wikipedia.org/wiki/Hibernate_(Java)"&gt;http://en.wikipedia.org/wiki/NoSQLhttp://en.wikipedia.org/wiki/Hibernate_(Java)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/LZysZwrbvcA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1773578262526819275/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/06/persistencia-con-jpa-y-apache-tapestry.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1773578262526819275?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1773578262526819275?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/LZysZwrbvcA/persistencia-con-jpa-y-apache-tapestry.html" title="Persistencia con JPA y Apache Tapestry" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s72-c/tapestry.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/06/persistencia-con-jpa-y-apache-tapestry.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMMRXgyfyp7ImA9WhFTFk4.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1764717043817285771</id><published>2013-05-31T18:15:00.000+02:00</published><updated>2013-06-07T21:48:04.697+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-06-07T21:48:04.697+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Seguridad en aplicación web con Apache Tapestry (II)</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apache Tapestry" border="0" height="133" src="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s320/tapestry.png" title="Apache Tapestry" width="320" /&gt;&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;/div&gt;
Si en la anterior entrada hablaba de la seguridad sobre los aspectos de la&amp;nbsp;&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/05/seguridad-en-aplicacion-web-con-apache.html"&gt;autenticación y autorización de las acciones que puede realizar los usuarios&lt;/a&gt;&amp;nbsp;en una aplicación web en esta hablaré como protegerse de otro tipo de fallos de seguridad, XSS e inyección de Sql.&lt;br /&gt;
&lt;br /&gt;
Otros dos aspectos muy a tener en cuenta desde el inicio y durante el desarrollo de una aplicación web son los siguientes:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;XSS (&lt;a href="http://es.wikipedia.org/wiki/Cross-site_scripting"&gt;Cross site scripting&lt;/a&gt;): es una vulnerabilidad que pueden sufrir las aplicaciones por básicamente no controlar los datos que un usuario envía a través de formularios o como parámetros en las URL. Por ejemplo, supongamos una aplicación recibe un formulario con un nombre que se escupe tal cual se envió en otra página de la aplicación y que otros usuarios pueden visualizar en sus navegadores posteriormente cuando accedan a las páginas que los muestran. Una posible situación puede darse cuando los datos enviados se guardan en una base de datos, un usuario los envía se guardan en la base de datos y otro usuario los ve. Ese dato puede ser una cadena inofensiva como el nombre que se pide pero un usuario malicioso puede enviar código javascript o una imagen con una URL que puede recolectar&amp;nbsp;con cualquier propósito la información de los usuario que la ven en su navegador. Un usuario enviando los datos adecuados puede explotar esta vulnerabilidad y conseguir desde obtener la sesión de otro usuario y hacer cualquier tipo de acción como si fuera ese otro usuario, hasta obtener datos y distribuir virus a los usuarios a través de nuestra propia página web.&amp;nbsp;
&lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/SQL_injection"&gt;Inyección Sql&lt;/a&gt;: esta vulnerabilidad puede ser explotada también por confiar en los valores que envía el usuario pero en vez afectar al html que genera la aplicación web afecta a las base de datos que utilice la aplicación. Si usamos los parámetros enviados por una fuente no confiable para construir las sql de forma dinámica concatenando trozos de sentencia con los parámetros, un parámetro con el valor adecuado puede modificar completamente la sentencia. Concatenando elementos se puede terminar una Sql y hacer cualquier otra a continuación. Las posibilidades de esto es que se podría extraer cualquier dato o borrar completamente la base de datos con sentencias delete o drop. Por ejemplo, hacer esto tiene el problema de la inyección de Sql: "select * from producto where id = " + id. Si el parámetro id tuviese el valor "1; delete from producto;" podríamos borrar todos los datos de la tabla.&lt;/li&gt;
&lt;/ul&gt;
Por tanto, tanto para evitar fallos de seguridad por XSS y de inyección Sql no se debe confiar en ningún dato enviado por el usuario o de un sistema externo. En realidad en ambos problemas de seguridad la situación es el misma pero que afecta a distintas partes de la aplicación, en un caso a la base de datos (inyección Sql) y en otro a la capa de presentación de la aplicación (XSS).&lt;br /&gt;
&lt;br /&gt;
¿Que hay que hacer para evitar estos problemas? Depende del caso. Para evitar XSS todo lo que se emita en el html de la página y se envíe al navegador del usuario ha de ser codificado como html haciendo que si hay un dato malicioso sea inofensivo ya que el navegador no lo interpretará como parte del lenguaje de marcas html sino como texto.
Para evitar la inyección de Sql si construimos alguna dinñamicamente los parámetros no se han de añadir concatenándolos. En Java con PreparedStatement, y seguro que en cualesquiera otros lenguajes, por un lado va la sql y por otro los parámetros, la clase o API que utilicemos se encargará de ejecutar la sentencia con los parámetros adecuados sin el problema de la inyección Sql (además tiene la ventaja de que el código será más legible al no estar mezclada con los parámetros concatenados).&lt;br /&gt;
&lt;br /&gt;
A continuación explicaré que funcionalidades proporciona Tapestry para que las aplicaciones desarrolladas con él sean más seguras en cuanto a XSS.&lt;br /&gt;
&lt;br /&gt;
Para evitar XSS todo lo que se genere como html a partir de datos recuperados de la base de datos y enviados por un usuario hay que escaparlo. Y Tapestry hace eso por defecto por lo que salvo que de forma expresa no hagamos el escapado no tendremos problemas de XSS. La generación de html se puede hacer de dos formas: en los archivos de plantilla tml o en código Java si se trata de un componente que no tiene plantilla tml asociada.&lt;br /&gt;
&lt;br /&gt;
Con una plantilla tml haremos lo siguiente y el nombre se mostrará escapado en el html:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5685908.js?file=Xss-1.tml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5685908/raw/Xss-1.tml"&gt;Xss-1.tml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Para evitar escapado hay que usar la opción:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5685908.js?file=Xss-2.tml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5685908/raw/Xss-2.tml"&gt;Xss-2.tml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En el componente Java usaremos la clase MarkupWriter y su método write para escapar los valores y el método writeRaw para evitar el escapado si estamos seguros de que no implica un problema de seguridad:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5685908.js?file=Xss.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5685908/raw/Xss.java"&gt;Xss.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Para evitar inyección de Sql usando Hibernate, JPA o la clase PreparedStatement y separando los parámetros de la sql o hql estaremos protegidos. Las buenas prácticas y un ejemplo de mala práctica usando la API de Hibernate, hql y sql para hacer las búsquedas son las siguientes:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5685908.js?file=InyeccionSql.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5685908/raw/InyeccionSql.java"&gt;InyeccionSql.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En la siguiente imagen puede verse como se puede inyectar javascript enviando un formulario con dato que no es escapado correctamente al mostrar su valor de nuevo.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-QiY0xo5lrKg/UajNb0loTDI/AAAAAAAACiI/RDg65GRNUFM/s1600/tapestry-xss.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="231" src="http://4.bp.blogspot.com/-QiY0xo5lrKg/UajNb0loTDI/AAAAAAAACiI/RDg65GRNUFM/s320/tapestry-xss.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Como en el resto de entradas el &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestrySecurity"&gt;código fuente completo&lt;/a&gt; lo puedes encontrar en &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master"&gt;mi repositorio de GitHub&lt;/a&gt;. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el &lt;a href="https://github.com/picodotdev/elblogdepicodev/archive/master.zip"&gt;código fuente del repositorio en un archivo zip&lt;/a&gt; con el anterior enlace.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5685908.js?file=git.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5685908/raw/git.sh"&gt;git.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://es.wikipedia.org/wiki/Cross-site_scripting"&gt;Cross site scripting&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://es.wikipedia.org/wiki/Inyecci%C3%B3n_SQL"&gt;Inyección SQL&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/MarkupWriter.html"&gt;MarkupWriter&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Output.html"&gt;Output&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/OutputRaw.html"&gt;OutputRaw&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/QTwdOaY87-0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1764717043817285771/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/05/seguridad-en-aplicacion-web-con-apache-II.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1764717043817285771?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1764717043817285771?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/QTwdOaY87-0/seguridad-en-aplicacion-web-con-apache-II.html" title="Seguridad en aplicación web con Apache Tapestry (II)" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s72-c/tapestry.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/05/seguridad-en-aplicacion-web-con-apache-II.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4NQXw7eip7ImA9WhFTEEw.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-782827450349143796</id><published>2013-05-24T19:09:00.000+02:00</published><updated>2013-05-31T18:16:30.202+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-31T18:16:30.202+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Seguridad en aplicación web con Apache Tapestry</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apache Tapestry" border="0" height="132" src="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s320/tapestry.png" title="Apache Tapestry" width="320" /&gt;&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apache Shiro" border="0" src="http://3.bp.blogspot.com/-0rj1VvDX_Nw/UZ9_nqEtnwI/AAAAAAAAChM/bBsxBV2MBNk/s1600/shiro.png" title="Apache Shiro" /&gt;&lt;/div&gt;
Además de la persistencia en una base de datos, otra de las funcionalidades comunes que suele necesitar una aplicación es la seguridad. En la seguridad hay varios aspectos a tratar que son:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Autenticación: que consiste en identificar al usuario en el sistema y comprobar que el usuario es quien dice ser. Normalmente la autenticación se suele realizar pidiéndole al usuario su identificativo, nombre de usuario o correo electrónico y una contraseña que solo él conoce. Aunque hay otras formas de realizarlo entre ellas los certificados.&lt;/li&gt;
&lt;li&gt;Autorización: que consiste en determinar si el usuario autenticado tienen permisos para realizar una determinada operación. La autorización puede realizarse mediante roles, permisos o una combinación de ambos dependiendo de lo adecuado para la operación. Pero en ocasiones no solo hay que validar si un usuario tiene permisos para para realizar una acción, también puede ser necesario restringir la operación sobre ciertos datos, los que se determinen que él está autorizado a modificar, si no se hiciese esto un usuario podría alterar los datos de otro y el sistema tener una brecha de seguridad.&lt;/li&gt;
&lt;/ul&gt;
La autenticación y la autorización son solo dos aspectos a considerar pero no son suficientes para considerar una aplicación segura. &lt;a href="http://elblogdepicodev.blogspot.com/2013/05/seguridad-en-aplicacion-web-con-apache-II.html"&gt;Otros aspectos a tener en cuenta son XSS&amp;nbsp;(Cross Site Scripting), la inyección de Sql&lt;/a&gt;&amp;nbsp;o las conexiones cifradas SSL/TLS pero estos serán tema para otras entradas.&lt;br /&gt;
&lt;br /&gt;
La información de autenticación y autorización puede guardarse en diferentes formas en lo que se conocen como Realms comunmente en Java. Algunos Realms puede ser simples archivos de texto plano aunque por su dificultad de mantenimiento al añadir nuevos usuarios, permisos o roles y que puede requerir un reinicio de la aplicación se suele optar por opciones como una base de datos relacional, un sistema LDAP o una base de datos nosql.&lt;br /&gt;
&lt;br /&gt;
Para tener un sistema seguro no basta con ocultar las opciones que un usuario no puede realizar. Ocultar las opciones está bien pero también hay que realizar las comprobaciones de autorización en el caso de una aplicación web en el servidor, al igual que no basta con realizar las comprobaciones de validación de datos en el cliente con javascript, en ambos casos las comprobaciones hay que hacerlas en el lado del servidor también, de lo contrario nada impediría a un usuario conociendo la URL y datos adecuados a enviar realizar algo que no debería (advertido estás si no quieres que te llamen un sábado de madrugada).&lt;br /&gt;
&lt;br /&gt;
La seguridad puede aplicarse de dos formas o una combinación de ambas:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;De forma declarativa: ya sea mediante anotaciones o en un archivo independiente del código. Esta es la opción preferida ya que de esta manera el código de la aplicación no está mezclado con el aspecto de la seguridad.&lt;/li&gt;
&lt;li&gt;De forma programática: si la opción declarativa no no es suficiente para algún caso podemos optar por hacerlo de forma programática, mediante código, con la que tendremos total flexibilidad para hacer cosas más específicas si necesitamos aunque mezclaremos el código de la aplicación con el código de seguridad.&lt;/li&gt;
&lt;/ul&gt;
Para aplicar seguridad en una aplicación Java disponemos de varias librerías, entre las más conocidas están:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://static.springsource.org/spring-security/site/index.html"&gt;Spring Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://shiro.apache.org/"&gt;Apache Shiro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
Las dos librerías son similares aunque se comenta que Apache Shiro es más fácil de aprender. Además de integraciones con estas librerías &lt;a href="http://tapestry.apache.org/"&gt;Apache Tapestry&lt;/a&gt; dispone de módulos para realizar &lt;a href="http://tynamo.org/tynamo-federatedaccounts+guide"&gt;autenticación con servicios de terceros como Facebook, Twitter o sistemas OpenID&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Pero veamos como aplicar seguridad a una aplicación web que use el framework Apache Tapestry. En el ejemplo usaré el módulo &lt;a href="http://tynamo.org/tapestry-security+guide"&gt;tapestry-security&lt;/a&gt; que a su vez usa Apache Shiro. El ejemplo consiste un una página a la que solo los usuarios autenticados pueden acceder, en ella los usuarios podrán realizar varias operaciones: sumar a una cuenta uno, dos, tres, restar uno o poner la cuenta a cero en función de los permisos y roles que tengan. Para autenticase se usa un formulario aunque perfectamente podría usarse una &lt;a href="http://en.wikipedia.org/wiki/Basic_access_authentication"&gt;autenticación BASIC&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Por simplicidad en el ejemplo los usuarios, passwords, roles y permisos los definiré en un archivo de texto, aunque en un proyecto real probablemente usaríamos una base de datos accediendo con hibernate para lo cual deberíamos implementar unos pocos métodos de la interfaz &lt;a href="http://shiro.apache.org/static/1.2.1/apidocs/org/apache/shiro/realm/Realm.html"&gt;Realm&lt;/a&gt; o si necesitamos autorización la interfaz &lt;a href="http://shiro.apache.org/static/1.2.1/apidocs/org/apache/shiro/realm/AuthorizingRealm.html"&gt;AuthorizingRealm&lt;/a&gt; de Shiro. El archivo shiro-users.properties sería el siguiente:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5644455.js?file=shiro-users.properties"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5644455/raw/shiro-users.properties"&gt;shiro-users.properties&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Por una parte se definen los usuarios con su password y roles que posee y por otro se definen los roles y los permisos que tiene cada uno.&lt;br /&gt;
&lt;br /&gt;
La única configuración que deberemos indicarle a Tapestry es la URL de la página que autenticará a los usuarios y la página a mostrar en caso de que el usuario no esté autorizado para realizar alguna operación y el Realm a usar, lo hacemos añadiendo el siguiente código al módulo de la aplicación:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5644455.js?file=AppModule.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5644455/raw/AppModule.java"&gt;AppModule.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

La página que realiza la autenticación es muy simple, poco más se encarga de recoger el usuario y password introducidos en el formulario de autenticación y a través del &lt;a href="http://shiro.apache.org/static/1.2.1/apidocs/org/apache/shiro/subject/Subject.html"&gt;Subject&lt;/a&gt; realiza el inicio de sesión.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5644455.js?file=Login.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5644455/raw/Login.java"&gt;Login.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Una vez configurado el módulo y hecha la página que realiza la autenticación solo debemos usar de forma declarativa las anotaciones que proporciona Shiro, en el caso de la página Index @RequiresUser para permitir solo a los usuarios autenticados entrar a esa página y sobre las operaciones @RequiresPermissions para requerir ciertos permisos para ejecutar las acciones o @RequiresRoles para requerir ciertos roles. Estas anotaciones podemos usarlas no solo en las páginas y componentes de Tapestry que forman parte de la capa de presentación sino también en los servicios que desarrollemos y que forman la capa de lógica de negocio.&lt;br /&gt;
&lt;br /&gt;
Si las anotaciones no son suficientes podemos hacerlo de forma programática, este es el probable caso de que un usuario solo debería modificar los datos relativos a él &amp;nbsp;sin poder modificar los de otros usuarios. El código variará en función de la forma de determinar si el usuario tiene permisos para un dato. Para comprobar si un usuario tiene ciertos permisos de forma programática debemos usar el objeto Subject que tiene muchos métodos para realizar comprobaciones, como para &amp;nbsp;reinicializar la cuenta se ha de tener el permiso «cuenta:reset» se debe hacer lo codificado en el método onActionFromReset:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5644455.js?file=Index.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5644455/raw/Index.java"&gt;Index.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En la plantilla de presentación que genera el html también deberemos hacer algunas comprobaciones de seguridad, básicamente para mostrar los enlaces a los que un usuario tenga permisos.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5644455.js?file=Index.tml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5644455/raw/Index.tml"&gt;Index.tml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

A continuación unas capturas de pantalla de la aplicación de la página de inicio autenticación, de la página con el índice de acciones y de la página de acción no autorizada:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-FocvGGuZOk0/UZ-UbxmKz2I/AAAAAAAAChg/H3oBuunrvt0/s1600/tapestry-security-login.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="140" src="http://3.bp.blogspot.com/-FocvGGuZOk0/UZ-UbxmKz2I/AAAAAAAAChg/H3oBuunrvt0/s200/tapestry-security-login.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-cafuHg9tPe8/UZ-UbyUB9UI/AAAAAAAAChk/dX1M8pR7Cvw/s1600/tapestry-security-index.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="140" src="http://1.bp.blogspot.com/-cafuHg9tPe8/UZ-UbyUB9UI/AAAAAAAAChk/dX1M8pR7Cvw/s200/tapestry-security-index.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-imKMQIMbM9c/UZ-UbxPGwOI/AAAAAAAAChc/KQoQht7CSb0/s1600/tapestry-security-unauthorized.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="140" src="http://1.bp.blogspot.com/-imKMQIMbM9c/UZ-UbxPGwOI/AAAAAAAAChc/KQoQht7CSb0/s200/tapestry-security-unauthorized.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Para hacer uso de tapestry-security deberemos incluir la librería como dependencia en el archivo build.gradle del proyecto:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5644455.js?file=build.gradle"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5644455/raw/build.gradle"&gt;build.gradle&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Como en el resto de entradas el &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestrySecurity"&gt;código fuente completo&lt;/a&gt; lo puedes encontrar en &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/BackboneREST"&gt;mi repositorio de GitHub&lt;/a&gt;. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el &lt;a href="https://github.com/picodotdev/elblogdepicodev/archive/master.zip"&gt;código fuente del repositorio en un archivo zip&lt;/a&gt; con el anterior enlace.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5644455.js?file=git.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5644455/raw/git.sh"&gt;git.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Para finalizar, a pesar de lo simple del ejemplo pero suficientemente representativo de lo que podría requerir una aplicación real comentar lo sencillo y limpio que ha sido aplicar la seguridad, por una parte gracias al uso de anotaciones y por otra gracias a Tapestry de por si.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/security.html"&gt;http://tapestry.apache.org/security.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/"&gt;http://tapestry.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://shiro.apache.org/"&gt;http://shiro.apache.org/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://static.springsource.org/spring-security/site/index.html"&gt;http://static.springsource.org/spring-security/site/index.html&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/icFSfXZaFZM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/782827450349143796/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/05/seguridad-en-aplicacion-web-con-apache.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/782827450349143796?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/782827450349143796?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/icFSfXZaFZM/seguridad-en-aplicacion-web-con-apache.html" title="Seguridad en aplicación web con Apache Tapestry" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s72-c/tapestry.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/05/seguridad-en-aplicacion-web-con-apache.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMFQXsyfSp7ImA9WhBbGU0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-19365630716426305</id><published>2013-05-17T17:50:00.001+02:00</published><updated>2013-05-18T20:56:50.595+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-18T20:56:50.595+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="gnome" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Copia de seguridad con rsync</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="rsync" border="0" src="http://4.bp.blogspot.com/-1FNpz4Bxpmk/UZZKifID5cI/AAAAAAAACg8/lFMm3-3F9MA/s1600/rsync.jpg" title="rsync" /&gt;&lt;/div&gt;
Las copias de seguridad son imprescindibles, si alguien piensa que no es que nunca se le ha estropeado el ordenador ya sea porque ha fallado el hardware o por una actualización que ha corrompido el sistema de arranque o peor por que el disco duro se ha estropeado, eso o no tiene datos importantes que tener a buen recaudo. Si aún no te ha pasado que se haya estropeado el ordenador y te has quedado sin esa información que no quieres perder quizá hayas tenido suerte pero has de saber que tarde o temprano te pasará y para no perder esa información los «backups» son imprescindibles.&lt;br /&gt;
&lt;br /&gt;
Una vez que tener copias de seguridad nos parece importante y tenemos respondida la pregunta de por qué, se nos plantean algunas otras preguntas:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;¿De que hacer copias de seguridad?&lt;/li&gt;
&lt;li&gt;¿Cada cuanto hacer copias de seguridad?&lt;/li&gt;
&lt;li&gt;¿Que características han de tener las copias de seguridad?&lt;/li&gt;
&lt;li&gt;¿En que medio guardar las copias?&lt;/li&gt;
&lt;li&gt;¿Como hacer las copias de seguridad?&lt;/li&gt;
&lt;li&gt;Y tan importantes como las anteriores, ¿como recuperar los datos?&lt;/li&gt;
&lt;/ul&gt;
Cada pregunta tiene diferentes respuestas según el usuario, entorno o la información de la que hacer las copias de seguridad. A la pregunta de que hacer copias de seguridad cada uno deberá conocer que información no quiere perder en caso de desastre, cuanto cambian los datos y que se perdería en un periodo de tiempo (día, semana, mes, ...), si las copias deben estar comprimidas para ahorrar espacio o cifradas y finalmente dependiendo de algunas respuestas anteriores que herramienta o herramientas usar.&lt;br /&gt;
&lt;br /&gt;
Yo hasta ahora no usaba ninguna herramienta usaba el simple y manual método de copiar y pegar archivos del disco duro del ordenador a un disco duro externo. Cuando no tenía disco duro externo las copias de seguridad las hacía en CD y DVD pero hacerlas en un medio óptico como los anteriores no los recomiendo porque son lentos de realizar, en estos días no tienen capacidad suficiente para guardar todo y por tanto deberemos tener varios volúmenes y finalmente lo más importante porque son unos medios poco fiables. Dado el precio y capacidad que tienen hoy en día los discos duros USB externos además de su velocidad de transferencia es más recomendable esta opción y es la que a día de hoy uso. Pero aún con un disco duro externo el método de copiar y pegar tarda un buen tiempo y sigue siendo lento, con 70 GiB de archivos personales que no quiero perder, pero también el hecho de hacerlo manualmente es propenso a que algún día se me olvide hacer la copia de seguridad de todo y por la ley de Murphy ese día fallará el ordenador. Con lo que con esta situación he empezado a buscar alternativas a mi método manual de copiar y pegar.&lt;br /&gt;
&lt;br /&gt;
Empece probando &lt;a href="https://launchpad.net/deja-dup"&gt;Déjà Dup&lt;/a&gt; pero no me ha convencido porque las copias de seguridad las guarda en un formato comprimido (y cifrado si se quiere) de modo que para recuperar los archivos se ha de utilizar la misma herramienta. Para mi una de las características de la copia de seguridad es que siga pudiendo tener accesibles los archivos independientemente de la herramienta. Viendo las posibilidades de los&amp;nbsp;&lt;a href="https://wiki.archlinux.org/index.php/Backup_Programs"&gt;programas de copia de seguridad&lt;/a&gt;&amp;nbsp;disponibles en Linux por la que me he decantado ha sido &lt;a href="http://rsync.samba.org/"&gt;rsync&lt;/a&gt;. Es un método simple que mantiene sincronizados el contenido de dos carpetas, copiando los nuevos archivos, borrando los eliminados y solo actualizando los necesarios además de poder hacerse las copias entre dos máquinas diferentes a través de la red. Mediante rsync para hacer una copia de seguridad ahora tardo mucho menos ya que solo se copian los archivos que hayan cambiado además de estar automatizado con un script con lo que resulta más fácil y por ello se pueden hacer más a menudo.&lt;br /&gt;
&lt;br /&gt;
Con el siguiente comando de &lt;a href="https://www.archlinux.org/packages/?name=rsync"&gt;rsync&lt;/a&gt;&amp;nbsp;se puede hacer una copia de seguridad de una carpeta a otra:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5599846.js?file=script-1.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5599846/raw/script-1.sh"&gt;script-1.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

La opción -a es una meta opción de utilidad que engloba otras con las más comunes para hacer copias de seguridad, -P hace que se muestre el progreso, --delete borra los archivos en la carpeta de destino que se hayan borrado en la carpeta origen.&lt;br /&gt;
&lt;br /&gt;
Otro ejemplo para copiar varias carpetas de la carpeta home de un usuario es, con la opción --files-from, hay que indicar también la opción -r para que las carpetas se copien de forma recursiva:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5599846.js?file=script-2.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5599846/raw/script-2.sh"&gt;script-2.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5599846.js?file=backup-files-from.txt"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5599846/raw/backup-files-from.txt"&gt;backup-files-from.txt&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Este simple comando ahorra mucho tiempo ya que con él no hay que copiar los XXX GiB cada vez que hay que hacer un copia de seguridad, solo se copiará lo que haya cambiado entre la copia de seguridad y el origen, que muy probablemente es una cantidad mucho menor que esos XXX GiB. Con lo simple que es se me hace extraño que haya pasado tanto tiempo haciendo las copias de seguridad manualmente.&lt;br /&gt;
&lt;br /&gt;
Si quieres algunas otros ejemplos de uso de &lt;a href="https://wiki.archlinux.org/index.php/Rsync"&gt;rsync&lt;/a&gt; como por ejemplo el como programar las copias de seguridad con cron o quieres conocer otras herramientas en la &lt;a href="https://wiki.archlinux.org/index.php/Backup_Programs"&gt;wiki de Arch Linux&lt;/a&gt; tienes unas cuantas más, algunas con interfaz gráfica.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="https://wiki.archlinux.org/index.php/Backup_Programs"&gt;https://wiki.archlinux.org/index.php/Backup_Programs&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://wiki.archlinux.org/index.php/Rsync"&gt;https://wiki.archlinux.org/index.php/Rsync&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/lGTELRyG3dw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/19365630716426305/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/05/copia-de-seguridad-con-rsync.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/19365630716426305?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/19365630716426305?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/lGTELRyG3dw/copia-de-seguridad-con-rsync.html" title="Copia de seguridad con rsync" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-1FNpz4Bxpmk/UZZKifID5cI/AAAAAAAACg8/lFMm3-3F9MA/s72-c/rsync.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/05/copia-de-seguridad-con-rsync.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UDQn86fCp7ImA9WhBbEkk.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-5081067060709588735</id><published>2013-05-10T18:19:00.000+02:00</published><updated>2013-05-11T05:01:13.114+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-11T05:01:13.114+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Modificar la base de datos con Liquibase</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Liquibase" border="0" src="http://4.bp.blogspot.com/-0mp_30qSpu8/UY0KpYkFc7I/AAAAAAAACgU/ExGFYrQfUYU/s1600/liquibase.gif" title="Liquibase" /&gt;&lt;/div&gt;
A lo largo del ciclo de desarrollo de todo proyecto se producen cambios algunos de los cuales casi con toda seguridad afectarán al esquema o modelo de la base de datos. Por una parte se pueden producir modificaciones de definición de datos (DDL, Data Definition Language) para añadir y borrar tablas y añadir, modificar y eliminar campos, crear índices y restricciones de integridad. Por otra parte se pueden producir manipulaciones de datos (DML, Data Manipulation Language) para añadir nuevos datos, modificar o eliminar algunos de los existentes. Estos cambios de la base de datos también puede ser útil que estén bajo el sistema de control de versiones que empleemos al igual que hacemos con los cambios del código fuente del programa para poder volver a un estado anterior. Una herramienta que nos permita hacer «refactor» de forma automatizada de la base de datos nos facilitará la tarea.&lt;br /&gt;
&lt;br /&gt;
Para llevar a cabo estas modificaciones tenemos varias opciones la primera que se nos puede ocurrir es crear un archivo con las sentencias SQL de los cambios y lanzarlo contra la base de datos. Sin embargo, esta opción aunque sencilla y rápida puede que no sea la mejor opción. La primera desventaja es que las sentencias puede que solo funcionen en uno o unos pocos sistemas de base de datos con lo que tal vez tengamos que desarrollar varios scripts para cada uno de los sistemas de base de datos a los que debamos dar soporte. La segunda desventaja es que si queremos revertir los cambios de un «refactor» deberemos crear las sentencias de «rollback» que puede dar bastante trabajo nuevamente si tenemos diferentes DBMS. Además de estas desventajas podríamos necesitar volver a cierto estado de la base de datos deberemos gestionarlo de alguna manera nosotros mismos probablemente de forma manual.&lt;br /&gt;
&lt;br /&gt;
Una de las herramientas que nos puede hacer más sencillo estas modificaciones que se producen a la base de datos es &lt;a href="http://www.liquibase.org/"&gt;Liquibase&lt;/a&gt;. Los scritps que actualizan la base de datos se definen en un XML (o json entre otros) de cambios que es independiente del sistema de base de datos que usemos aunque se pueden incluir sentencias SQL específicas para uno de ellos, también se pueden definir las sentencias rollback que permiten volver a un estado anterior. Soporta las principales bases de datos entre ellas:

&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Oracle&lt;/li&gt;
&lt;li&gt;MS-SQL&lt;/li&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;li&gt;Y otras entre ellas Sybase Enterprise, Sybase, DB2, Apache Derby, HSQL, H2, Informix, InterSystems, Firebird, SAPDB.&lt;/li&gt;
&lt;/ul&gt;
Una de las primeras cosas que podríamos necesitar si empezamos a usar esta herramienta sobre una base de datos existente es obtener el primer xml changelog.xml con el estado actual de la base de datos.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5555053.js?file=update-1.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5555053/raw/update-1.sh"&gt;update-1.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-pkw0yyM-aNs/UY0ZsQf37oI/AAAAAAAACgk/LWaJLw6vN-g/s1600/liquibase-generateChangeLog.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="146" src="http://3.bp.blogspot.com/-pkw0yyM-aNs/UY0ZsQf37oI/AAAAAAAACgk/LWaJLw6vN-g/s320/liquibase-generateChangeLog.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Si queremos lanzar una actualización de la base de datos a partir de un archivo changelog.xml con algunas modificaciones ejecutaremos lo siguiente:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5555053.js?file=update-2.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5555053/raw/update-2.sh"&gt;update-2.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-X1SefoSQ9Nw/UY0aZiOXzFI/AAAAAAAACgs/tIb9SpcC1Y8/s1600/liquibase-update.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="139" src="http://2.bp.blogspot.com/-X1SefoSQ9Nw/UY0aZiOXzFI/AAAAAAAACgs/tIb9SpcC1Y8/s320/liquibase-update.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Para obtener las SQLs que son lanzadas en la consola sin que sean ejecutadas contra la base de datos usaremos «updateSQL» en vez de «update» de esta manera podremos revisar las sentencias que ejecutará Liquibase. También puede sernos de utilidad obtener las &lt;a href="http://www.liquibase.org/documentation/diff.html"&gt;diferencias entre dos versiones de la bases de datos&lt;/a&gt; o &lt;a href="http://www.liquibase.org/documentation/rollback.html"&gt;hacer rollback de los cambios&lt;/a&gt;, cosa que podemos hacer de diferentes formas.&lt;br /&gt;
Un archivo changelog.xml tiene el siguiente aspecto, en él vemos como se definen las tablas, campos y restricciones de integridad:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5555053.js?file=changelog.xml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5555053/raw/changelog.xml"&gt;changelog.xml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Por supuesto, estas tareas se pueden automatizar con la herramienta de construcción que prefiramos ya sea Ant, Maven, Gradle o mediante linea de comandos.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://www.liquibase.org/"&gt;Liquibase&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/F5H4pdgoQ8c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/5081067060709588735/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/05/modificar-la-base-de-datos-con-liquibase.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5081067060709588735?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5081067060709588735?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/F5H4pdgoQ8c/modificar-la-base-de-datos-con-liquibase.html" title="Modificar la base de datos con Liquibase" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-0mp_30qSpu8/UY0KpYkFc7I/AAAAAAAACgU/ExGFYrQfUYU/s72-c/liquibase.gif" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/05/modificar-la-base-de-datos-con-liquibase.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IDRXg_fyp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7161091350506843721</id><published>2013-05-03T21:27:00.003+02:00</published><updated>2013-05-03T21:32:54.647+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:32:54.647+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Jasmine" border="0" height="63" src="http://2.bp.blogspot.com/-CI-bmJ97HgE/UYP7rdVA0KI/AAAAAAAACf0/dGYUu61qUwg/s200/jasmine.png" title="Jasmine" width="200" /&gt;&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Backbone.js" border="0" height="35" src="http://2.bp.blogspot.com/-SMyoDvx_-j8/UXFvpfyC7iI/AAAAAAAACec/LiLs_goZU4Y/s200/backbone.png" title="Backbone.js" width="200" /&gt;&lt;/div&gt;
Tener un conjunto de pruebas unitarias sobre el código de la aplicación que desarrollamos es importante para tener más seguridad de que lo que hacemos funciona como se espera y no introducimos errores en funcionalidades que ya existían al hacer modificaciones, además de que en el caso de que haya errores estos serán más fácilmente descubiertos y resueltos en menos tiempo lo que hará que seamos más productivos. Si en una aplicación web es posible que ya hagamos pruebas unitarias sobre el código del lado del servidor, ¿por que no hacerlo también en el lado del cliente? Si la aplicación tiene un peso importante de javascript en el lado del cliente, y esa es la tendencia, es recomendable tener también teses unitarios de esta parte del código.&lt;br /&gt;
&lt;br /&gt;
Aprovechando el ejemplo que hice de una &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;aplicación bastante completa de una lista de tareas&lt;/a&gt; que utilizaba &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Backbone&lt;/a&gt;, &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;RequireJS&lt;/a&gt;, &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Mustache&lt;/a&gt; en el lado cliente y &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/integracion-tapestry-con-resteasy.htm.html"&gt;Tapestry y RESTEasy&lt;/a&gt; en el lado del servidor ahora le añadiré al código javascript el conjunto de pruebas unitarias con &lt;a href="http://pivotal.github.io/jasmine/"&gt;Jasmine&lt;/a&gt; y &lt;a href="http://sinonjs.org/"&gt;Sinon&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Jasmine es una herramienta para realizar pruebas basadas en BDD (behavior-driven development). BDD es parte de las metododologías TDD (test-driven development) haciendo énfasis en que la gente del dominio pueda trabajar con la gente técnica. Jasmine nos servirá para hacer las pruebas unitarias, Sinon nos permitirá usar spies, stubs y mocks en esas pruebas:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Spy: Un spy es una función que se envía como parámetro al sujeto bajo prueba para que recolecte datos sobre lo que sucede dentro de esa función y objeto, se llama espía porque se envía tras la lineas enemigas para que recolecte información. La información que puede recolectar es el número de veces que se llama una función, con que parámetros y cuales, los valores de retorno o si lanzó excepciones.&lt;/li&gt;
&lt;li&gt;Stub: ofrecen un comportamiento parcial preprogramado del objeto real, se puede utilizar para que proporcione los datos que queramos al sujeto bajo prueba. Los stubs también pueden ejercer de espías.&lt;/li&gt;
&lt;li&gt;Mock: que también son espías (funciones falsas) y stubs (ofrecen comportmiento preprogramado) conocen las expectativas de como han de ser usados, requiriendo que se llamen con ciertos parámetros, un número de veces, en cierto orden, .... Conocen cual es el comportamiento esperado del sujeto bajo prueba y una vez hecha la prueba se puede comprobar.&lt;/li&gt;
&lt;/ul&gt;
A continuación veremos el código de las pruebas para la aplicación, las pruebas se centrarán en el código en el código de la lista de tareas y de probar las diferentes partes por separado, el modelo, vista y controlador. Veamos primeramente las pruebas para los modelos Tarea y Tareas, las pruebas se centrarán en las funciones adicionales que hemos asociado a los modelos, es decir, probaran lo que hemos hecho no Backbone.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5512335.js?file=tareas-specs-1.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5512335/raw/tareas-specs-1.js"&gt;tareas-specs-1.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;script src="https://gist.github.com/picodotdev/5512335.js?file=tareas-specs-2.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5512335/raw/tareas-specs-2.js"&gt;tareas-specs-2.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En la parte del controlador de la vista nos centraremos en probar los métodos y eventos como onChangeCompletada en TareaView y addTarea, resetTareas, onClickLimpiar y onKeypressNuevaTarea en TareasView. Dado que la lista de tareas hace uso de un servicio REST utilizaremos Sinon para atrapar esas peticiones AJAX que se harían en la realidad.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5512335.js?file=tareas-specs-3.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5512335/raw/tareas-specs-3.js"&gt;tareas-specs-3.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En la vista probaremos que dado un modelo el resultado del html contiene los elementos que esperamos:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5512335.js?file=tareas-specs-4.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5512335/raw/tareas-specs-4.js"&gt;tareas-specs-4.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Las pruebas se lanzan abriendo la página html SpecRunner.html en el navegador. Esa página html tiene la dependencia sobre RequireJS y este carga en el navegador el resto de dependencias que necesiten los diferentes módulos incluido el módulo bajo prueba de la lista de tareas. Las pruebas de Jasmine se definen en otro módulo de RequireJS que tiene como dependencia la librería de Jasmine, Sinon y por supuesto el módulo tareas que probará. El resultado es el siguiente:
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-Keuu1mQpLns/UYQMj600ntI/AAAAAAAACgE/1brxRIrSwSw/s1600/todo-test.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-Keuu1mQpLns/UYQMj600ntI/AAAAAAAACgE/1brxRIrSwSw/s320/todo-test.png" width="269" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;script src="https://gist.github.com/picodotdev/5512335.js?file=SpecRunner.html"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5512335/raw/SpecRunner.html"&gt;SpecRunner.html&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Backbone se presta muy bien a las pruebas unitarias. Una gran característica es que para realizarlas no es necesario que las vistas se asocien a un DOM real, funcionan en memoria, de modo que no hace falta insertar ni quitar contenido de la página mientras se van ejecutando los teses, no hay necesidad de limpiar el DOM para los siguiente teses y así funciona más rápido. Como usa el patrón MVC cada una de las partes del modelo, vista y controlador pueden probarse por separado. Este ejemplo no muestra todas la posibilidades de Jasmine y Sinon pero da una idea de como son las pruebas con ellas.&lt;br /&gt;
&lt;br /&gt;
En mi repositorio de GitHub puedes encontrar el &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/BackboneREST"&gt;código fuente completo del ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Y con esta es por el momento la última entrada sobre javascript que escriba de esta serie, no descarto escribir alguna más sobre javascript en el futuro porque alrededor de este lenguaje de programación están surgiendo muchas utilidades y librerías.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo de RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Introducción y ejemplo de Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;Logging en Javascript con log4javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/capturar-errores-de-javascript.html"&gt;Capturar errores de Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/optimizar-modulos-de-requirejs.html"&gt;Optimizar módulos de RequireJS y archivos Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.genbetadev.com/desarrollo-web/patron-de-diseno-mvc-del-lado-cliente-con-backbonejs"&gt;Patrón de diseño MVC del lado cliente con Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Introducción y ejemplo de Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;a href="http://tinnedfruit.com/2011/03/03/testing-backbone-apps-with-jasmine-sinon.html"&gt;http://tinnedfruit.com/2011/03/03/testing-backbone-apps-with-jasmine-sinon.html   &lt;/a&gt;&lt;br /&gt;
&lt;a href="http://blog.involver.com/2012/01/26/testing-backbone-js-best-practices-2/"&gt;http://blog.involver.com/2012/01/26/testing-backbone-js-best-practices-2/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://kilon.org/blog/2012/08/testing-backbone-requirejs-applications-with-jasmine/"&gt;http://kilon.org/blog/2012/08/testing-backbone-requirejs-applications-with-jasmine/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://backbone.js/"&gt;Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://mustache.github.io/"&gt;Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://pivotal.github.io/jasmine/"&gt;Jasmine&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://sinonjs.org/"&gt;Sinon&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.jboss.org/resteasy/"&gt;RESTEasy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/"&gt;Apache Tapestry&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/XYP4sF2n9uU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7161091350506843721/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/05/ejemplo-de-pruebas-unitarias-en.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7161091350506843721?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7161091350506843721?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/XYP4sF2n9uU/ejemplo-de-pruebas-unitarias-en.html" title="Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-CI-bmJ97HgE/UYP7rdVA0KI/AAAAAAAACf0/dGYUu61qUwg/s72-c/jasmine.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/05/ejemplo-de-pruebas-unitarias-en.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QGSXs9fyp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-4221696891817628232</id><published>2013-04-26T21:38:00.000+02:00</published><updated>2013-05-03T21:28:48.567+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:28:48.567+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Backbone.js" border="0" height="56" src="http://2.bp.blogspot.com/-SMyoDvx_-j8/UXFvpfyC7iI/AAAAAAAACec/LiLs_goZU4Y/s320/backbone.png" title="Backbone.js" width="320" /&gt;&lt;/div&gt;
Normalmente escribo artículos con ejemplos que tratan sobre una única tecnología, al tratar cada tecnología por separado entender que puede ofrecer es más sencillo pero en una aplicación real normalmente se usan varias tecnologías combinadas y al mismo tiempo. Esta entrada aunque sigue siendo un ejemplo es mucho más parecido a lo que podría ser una aplicación real que lo que he explicado durante las últimas entradas. En esta serie sobre javascript he explicado un montón de cosas por separado entre ellas &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;RequireJS&lt;/a&gt;,&amp;nbsp;&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Mustache&lt;/a&gt;,&amp;nbsp;&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Backbone&lt;/a&gt;, &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/integracion-tapestry-con-resteasy.htm.html"&gt;servicios rest con Tapestry&lt;/a&gt; y &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html"&gt;RESTEasy&lt;/a&gt;. En esta entrada haré un ejemplo haciendo uso de todas las anteriores y alguna cosa más adicional como &lt;a href="http://twitter.github.io/bootstrap/"&gt;twitter bootstrap&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
El ejemplo consiste en una lista de tareas, pudiéndose introducir nuevas tareas y marcarlas como realizadas. También se podrá eliminar de la lista las tareas completadas y ver un resumen con el número de tareas completadas y de tareas totales.&lt;br /&gt;
&lt;br /&gt;
En la parte cliente de la aplicación se hace uso de &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt; para manejar la carga de los archivos javascript necesarios, de &lt;a href="http://backbonejs.org/"&gt;backbone&lt;/a&gt; para gestionar la lista de tareas y realizar la comunicación con el servicio REST del servidor y de &lt;a href="http://mustache.github.io/"&gt;Mustache&lt;/a&gt; como motor de plantillas para generar la vista (el html). En la parte del servidor usa &lt;a href="http://tapestry.apache.org/"&gt;Tapestry&lt;/a&gt; como framework web y &lt;a href="http://www.jboss.org/resteasy"&gt;RESTEasy&lt;/a&gt; como librería para implementar un servicio REST que proporcionará la persistencia en memoria de la lista de tareas.&lt;br /&gt;
&lt;br /&gt;
Backbone permite organizar el código aplicando el patrón de diseño MVC (modelo-vista-controlador) ampliamente extendido en los frameworks web. Este patrón de diseño tiene la ventaja que separa la aplicación en tres partes diferenciadas:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;El modelo: que contiene los datos del dominio que maneja la aplicación que cuando es modificado lanza eventos para que el controlador y la vista actúen en consecuencia.&lt;/li&gt;
&lt;li&gt;El controlador: que reacciona ante los eventos que se produzcan en el modelo o por el usuario y modifica adecuadamente el modelo o la vista según el evento.&lt;/li&gt;
&lt;li&gt;La vista: que a partir del modelo produce la interfaz que se ofrece al usuario para que pueda manipularlos, los eventos producidos en la vista son manejados por el controlador.&lt;/li&gt;
&lt;/ul&gt;
Como se ve las tres partes se relacionan entre si pero están separadas de forma que cada una se encargue de una tarea. El código queda bien organizado y se evita el código espagueti que podríamos obtener de otra forma.&lt;br /&gt;
&lt;br /&gt;
En una aplicación con la combinación de Backbone, Mustache y un servicio REST el servidor devuelve el html de la página inicial, las plantillas y los datos del servidor y se delega en el navegador del usuario la tarea de renderizar el html final. Esto tiene la ventaja de que entre el navegador y el servidor viajan menos datos (los datos y las plantillas tendrán menos tamaño que los datos formateados a html) y la parte del servidor se simplifica (no es necesaria la lógica para formatear a html los datos que dependiendo del framework hacen los jsp, tml de tapestry o gsp de grails). Aunque el código de la parte cliente crecerá, el código de la parte del servidor se hará más simple. Una vez cargada la página inicial entre el servidor y el cliente solo viajan los datos en formato json.&lt;br /&gt;
&lt;br /&gt;
Veamos el ejemplo a continuación empezando por el modelo. El modelo con Backbone se define con &lt;a href="http://backbonejs.org/#Model-extend"&gt;Backbone.Model.extend&lt;/a&gt;, donde podremos indicar las propiedades de los objetos, también podemos definir funciones de utilidad. En este caso el modelo estará formado por los objetos Tarea que tendrán las siguientes propiedades:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Un id que identificará la tarea.&lt;/li&gt;
&lt;li&gt;La descripción de la misma.&lt;/li&gt;
&lt;li&gt;Y un indicador de si está completada.&lt;/li&gt;
&lt;/ul&gt;
Pero en la lista de tareas no solo tendremos un objeto Tarea, tendremos una colección de ellas. Esta colección también forma parte del modelo y se define con &lt;a href="http://backbonejs.org/#Collection-extend"&gt;Backbone.Collection.extend&lt;/a&gt;, los elementos de esta colección serán objetos del modelo Tarea y lo indicamos en la propiedad model, también podemos definir funciones de utilidad como una función para obtener un array con las tareas completadas y otra para eliminar las tareas completadas que nos interesarán para ofrecer la funcionalidad de la lista de tareas. Las propiedades urlRoot y url son importantes para que backbone sepa localizar el servicio REST que proporcionará la persistencia.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5469220.js?file=tareas-1.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5469220/raw/tareas-1.js"&gt;tareas-1.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5469220.js?file=tareas-2.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5469220/raw/tareas-2.js"&gt;tareas-2.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

La interfaz del servicio web REST es:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5469220.js?file=TareasResource.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5469220/raw/TareasResource.java"&gt;TareasResource.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En Backbone la vista y el controlador de define en una misma entidad con &lt;a href="http://backbonejs.org/#View-extend"&gt;Backbone.View extend&lt;/a&gt;. Aunque se definen ambas cosas a la vez la vista principalmente está formada por el método render, el resto será parte del controlador. Otra sección importante de la vista es la propiedad events donde indicaremos que eventos manejará el controlador y sobre que elementos de la vista, básicamente se define como clave el evento (click, change, ...) y un selector similar a los usados en jquery y como valor el nombre de la función que manejará el evento. En la vista también podremos definir métodos de utilidad. Tendremos dos vistas: una para para una tarea y otra para la aplicación de la lista de tareas.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5469220.js?file=tareas-3.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5469220/raw/tareas-3.js"&gt;tareas-3.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En el método render se hace uso de Mustache para generar la vista a partir de una plantilla que contiene el código html, la plantilla combinado con los datos del modelo genera un resultado que será incluido en el html de la página. En las funciones initialize se inicializa la vista principalmente para definir que eventos que produzca el modelo serán tratados por el controlador, estos son las funciones on sobre el modelo, por ejemplo, nos interesará saber que se añade, modifica o elimina una tarea de la colección de tareas para actualizar la vista adecuadamente.&lt;br /&gt;
&lt;br /&gt;
En el caso de TareasApp en el método initialize además se inserta en el elemento «el» que se le pasa en el constructor la vista inicial de la aplicación. De esta forma podríamos crear varias instancias de TareasApp.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5469220.js?file=tareas-4.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5469220/raw/tareas-4.js"&gt;tareas-4.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Las plantillas de Mustache las he definido entre elementos &amp;lt;![CDATA[ ... ]]&amp;gt; ya que las plantillas de Tapestry han de ser xhtml válido, algunas expansiones de Mustache como {{attrs.checked}} de la plantilla tarea-template Tapestry no las entiende como válidas ya que realmente la plantilla Mustache en este caso no es html válido.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5469220.js?file=Index.tml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5469220/raw/Index.tml"&gt;Index.tml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Una vez desarrollada la parte del servicio REST en el servidor con RESTEasy integrarlo con Backbone es muy sencillo, únicamente deberemos llamar en los puntos adecuados a las funciones save y destroy de los modelos. Al método save se le llama cuando se añade una nueva tarea en TareasApp.addTarea y cuando se modifica en TareaView.onChangeCompletada. Cuando se hace clic en el botón para limpiar las tareas completadas se llama a la función destroy de cada uno de los modelos. Estos métodos lanzarán las peticiones AJAX al servicio REST de forma automática variando su método y con su correspondiente url según hemos definido con las anotaciones del servicio REST: POST (crear), PUT (actualizar) y DELETE (eliminar).&lt;br /&gt;
&lt;br /&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-TN2wrXMI31A/UXrMi6g0xAI/AAAAAAAACfQ/Zxij57ZAIuQ/s1600/backbone-rest-2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="108" src="http://2.bp.blogspot.com/-TN2wrXMI31A/UXrMi6g0xAI/AAAAAAAACfQ/Zxij57ZAIuQ/s200/backbone-rest-2.png" width="200" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Petición al crear una nueva tarea&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-9ZzBJVBBtiw/UXrMjI0LewI/AAAAAAAACfI/uG7mymO0hDg/s1600/backbone-rest-3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="108" src="http://1.bp.blogspot.com/-9ZzBJVBBtiw/UXrMjI0LewI/AAAAAAAACfI/uG7mymO0hDg/s200/backbone-rest-3.png" width="200" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Petición al marcar como completada una tarea&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-XTVBCwVCLL0/UXraEU7NGZI/AAAAAAAACfg/ZzkNqVDWhbE/s1600/backbone-rest-4.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="108" src="http://4.bp.blogspot.com/-XTVBCwVCLL0/UXraEU7NGZI/AAAAAAAACfg/ZzkNqVDWhbE/s200/backbone-rest-4.png" width="200" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Petición al limpiar tareas completadas&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
La carga de inicial de la lista de tareas puede hacerse de dos formas: devolviendo los datos de las tareas cuando se pide la página o una vez cargada la página mediante una petición AJAX adicional. En Backbone recomiendan la primera ya que así se evitan peticiones al servidor innecesarias. Además aunque hagamos que las peticiones AJAX sean rápidas probablemente sean lo suficientemente lentas para que el usuario vea como se va rellenando el contenido de la página y dependiendo del html verá como los elementos van cambiando de posición lo cual puede ser molesto. Aunque hay que decir que incluir los datos en la propia página impediría cachear la página. Al final de la página Index.tml, está la sección para cargar la lista inicial de tareas que se pasará a TareasApp después de su constructor en el método resetTareas.&lt;br /&gt;
&lt;br /&gt;
Esta es una captura del ejemplo:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-ViuHMfwgVd8/UXrIAr1mYrI/AAAAAAAACe4/ZSez4LTyuCQ/s1600/backbone-rest-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="216" src="http://1.bp.blogspot.com/-ViuHMfwgVd8/UXrIAr1mYrI/AAAAAAAACe4/ZSez4LTyuCQ/s320/backbone-rest-1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Después de haber usado Backbone en este ejemplo simplemente tengo que decir que es una gran herramienta y que facilita y ayuda a organizar el código javascript en gran medida. Permite separar los datos de la aplicación que forman el modelo del controlador y la vista que reaccionan mediante los eventos producidos en el modelo. También permite desarrollar aplicaciones con un gran peso de javascript en la parte cliente sin que el código se convierta posteriormente en un infierno de mantenimiento aún con la ayuda de jQuery. Es una ayuda tan grande para hacer algunas cosas de la interfaz del cliente como lo es jQuery para manipular los elementos html.&lt;br /&gt;
&lt;br /&gt;
Algunas partes no las he explicado como las plantillas de Mustache, el uso de RequireJS o la parte del servidor del servicio RESTEasy ya que ya lo que he hecho en entradas anteriores que puedes visitarlas mediante sus enlaces.&lt;br /&gt;
&lt;br /&gt;
Como en el resto de entradas el código fuente completo lo puedes encontrar en &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/BackboneREST"&gt;mi repositorio de GitHub&lt;/a&gt;. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el &lt;a href="https://github.com/picodotdev/elblogdepicodev/archive/master.zip"&gt;código fuente del repositorio&amp;nbsp;en un archivo zip&lt;/a&gt;&amp;nbsp;con el anterior enlace.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5469220.js?file=git.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5469220/raw/git.sh"&gt;git.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Referencia:
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo de RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Introducción y ejemplo de Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;Logging en Javascript con log4javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/capturar-errores-de-javascript.html"&gt;Capturar errores de Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/optimizar-modulos-de-requirejs.html"&gt;Optimizar módulos de RequireJS y archivos Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.genbetadev.com/desarrollo-web/patron-de-diseno-mvc-del-lado-cliente-con-backbonejs"&gt;Patrón de diseño MVC del lado cliente con Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Introducción y ejemplo de Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://ejemplo%20de%20pruebas%20unitarias%20en%20javascript%20con%20jasmine%20y%20sinon/"&gt;Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/0Pml9IocHOw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/4221696891817628232/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/04/ejemplo-lista-de-tareas-con-backbone.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4221696891817628232?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4221696891817628232?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/0Pml9IocHOw/ejemplo-lista-de-tareas-con-backbone.html" title="Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-SMyoDvx_-j8/UXFvpfyC7iI/AAAAAAAACec/LiLs_goZU4Y/s72-c/backbone.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/04/ejemplo-lista-de-tareas-con-backbone.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QAQXg4fyp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-5546643635990216421</id><published>2013-04-19T20:22:00.001+02:00</published><updated>2013-05-03T21:29:00.637+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:29:00.637+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Introducción y ejemplo de Backbone.js</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Backbone.js" border="0" height="56" src="http://3.bp.blogspot.com/-SMyoDvx_-j8/UXFvpfyC7iI/AAAAAAAACeY/uGQRcCderAg/s320/backbone.png" title="Backbone.js" width="320" /&gt;&lt;/div&gt;
Las aplicaciones y páginas web están haciendo uso de código javascript cada vez en mayor cantidad para proporcionar aplicaciones con funcionalidades más complejas. En estos momentos las librerías para utilizarse en javascript son muchas y una aplicación puede hacer uso de varias de ellas.&lt;br /&gt;
&lt;br /&gt;
A pesar de las facilidades que proporcionan librerías como jQuery para manipular los elementos de una página web de forma dinámica, mucho código mal estructurado puede llegar a convertirse en un perfecto código espagueti, aunque jQuery facilita y simplifica muchas tareas en el manejo de los elementos de una página no es suficiente, a pesar de jQuery se puede llegar a tener un código con un mar de selectores, mantener sincronizados con la interfaz con el estado de la aplicación y la base de datos puede convertirse en una tarea complicada dar como resultado código con el que apetece poco trabajar.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://backbonejs.org/"&gt;Backbone.js&lt;/a&gt; trata de de resolver algunos de estos problemas proporcionando un marco sobre que el que organizar el código. Backbone.js en esencia en una librería modelo-vista-controlador (MVC) para javascript. Basándose en este patrón de diseño cada parte es independiente del resto. El modelo conserva el estado de la aplicación y produce eventos al ser modificado de forma que la vista se pueda actualizar de una forma independiente para ambos, la vista proporciona la interfaz del controlador a través del que se modifica el modelo de forma adecuada, todos los elementos están relacionados pero cada uno se encarga de una parte.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;img alt="Patrón de diseño Modelo Vista Controlador (MVC)" border="0" height="150" src="http://3.bp.blogspot.com/-_DCZpuhhtv4/UXFyrPebqCI/AAAAAAAACeg/1RIrrjzUpNk/s320/model-view-controller.png" title="Patrón de diseño Modelo Vista Controlador (MVC)" width="320" /&gt;&lt;/div&gt;
&lt;br /&gt;
Backbone además proporciona una forma para trabajar con servicios REST tanto para recuperar los datos de los modelos como para actualizarlos, guardarlos y eliminarlos en el servidor.

A continuación pondré un pequeño ejemplo usando backbone.js que calcula el área de un rectángulo dado su alto y ancho y que lo muestra en pantalla. Como backbone.js sigue el patrón de diseño MVC por un lado tendremos el modelo, en nuestro caso el rectángulo con las propiedades alto y ancho que nos servirán para calcular el área, por el otro la vista que se actualizará según los datos del modelo introducidos en dos campos de texto, finalmente el controlador que captura los eventos como los cambios en los campos de texto, modifica el modelo y el modelo notifica a la vista para que se actualice.&lt;br /&gt;
&lt;br /&gt;
Vemos un ejemplo simple y que muestra parte del potencial de backbone.js. El modelo se define con el siguiente código, además de las propiedades se puede definir funciones que nos puede ser de utilidad, como calcular el área o obtener un objeto para ser utilizado con Mustache:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5421633.js?file=main-1.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5421633/raw/main-1.js"&gt;main-1.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
La otra parte es la vista y el controlador, ambas cosas se definen con Backbone.View.extend:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5421633.js?file=main-2.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5421633/raw/main-2.js"&gt;main-2.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
La inicialización del ejemplo:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5421633.js?file=main-3.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5421633/raw/main-3.js"&gt;main-3.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
Las partes de esta vista-controlador son:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;el: el elemento html sobre el que actuará la vista.&lt;/li&gt;
&lt;li&gt;events: los eventos que manejará el controlador, es un mapa donde la clave es un selector muy parecido a jquery y el nombre de la función a llamar cuando se produzca.&lt;/li&gt;
&lt;li&gt;initialize: con la función de underscore _.bindAll haremos que la referencia a this sea la vista en las funciones indicadas (render, onChangeAlto, onChangeAncho). A continuación se crea el modelo y cuando se produzca algún cambio en él (evento change) se actualice la vista. Finalmente, cuando se inicia la aplicación se dibuja por primera vez la vista.&lt;/li&gt;
&lt;li&gt;onChangeAlto y onChangeAncho: obtienen el alto o ancho de los campos de texto y modifican el modelo.&lt;/li&gt;
&lt;li&gt;render: se encarga de actualizar la vista cuando haya algún cambio en el modelo tal y como hemos echo en la función initialize con la función on sobre el modelo. Actualiza los inputs de alto y ancho con los valores del modelo y mediante una plantilla de Mustache forma el mensaje que se visualiza en el &amp;nbsp;elemento con id resultado.&lt;/li&gt;
&lt;/ul&gt;
Finalmente, queda el pequeño código html de la aplicación que contiene los campos de texto para introducir el alto y ancho y el elemento div donde se mostrará el área calculado del rectángulo:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5421633.js?file=HelloWorldBackbone.html"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5421633/raw/HelloWorldBackbone.html"&gt;HelloWorldBackbone.html&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-okBxtRGOpAs/UXGHhhKIm1I/AAAAAAAACeo/zRjC-0Oo4aw/s1600/helloworldbackbone.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="105" src="http://3.bp.blogspot.com/-okBxtRGOpAs/UXGHhhKIm1I/AAAAAAAACeo/zRjC-0Oo4aw/s400/helloworldbackbone.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Backbone.js no es la única librería que implementa el patrón MVC en javascript otras alternativas son &lt;a href="http://knockoutjs.com/"&gt;knockout&lt;/a&gt; y &lt;a href="http://angularjs.org/"&gt;AngularJS&lt;/a&gt; aunque esta últimas tienen la notable diferencia de que se basan en añadir atributos especiales a las etiquetas html para definir el modelo y asociar los eventos. Backbone.js no necesita añadir esos atributos especiales a la etiquetas html&amp;nbsp;o «instrumentalizarlo», esto se hace a través de javascript lo que da una mayor separación entre el código html y el código javascript. Por ahí se dice que usando backbone.js para realizar lo mismo se necesitan más lineas de javascript pero hay que tener en cuenta que a cambio el html necesario es mucho más simple que en knockout o AngularJS lo que facilita que el trabajo con motores de plantillas como &lt;a href="http://mustache.github.io/"&gt;Mustache&lt;/a&gt; o &lt;a href="http://handlebarsjs.com/"&gt;Handlebars&lt;/a&gt; también sea más simple.&lt;br /&gt;
&lt;br /&gt;
Como en el resto de entradas sobre esta serie de javascript el código fuente del ejemplo completo lo puedes encontrar en mi &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HelloWorldBackbone"&gt;repositorio de GitHub&lt;/a&gt;. Este ejemplo se basa en &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;RequireJS&lt;/a&gt; y &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Mustache&lt;/a&gt; visto en anteriores entradas que puedes consultar en el apartado de referencia al final de la entrada.&lt;br /&gt;
&lt;br /&gt;
En la siguiente entrada mostraré un ejemplo completo y usando muchas cosas al mismo tiempo de las que he estado hablando individualmente en las últimas entradas, será un ejemplo de una aplicación de lista de tareas por hacer que permite crearlas y completarlas, usará RequireJS, Mustache y Backbone (usándolo de forma más avanzada y completa de lo visto en esta entrada) en la parte cliente y &lt;a href="http://tapestry.apache.org/"&gt;Tapestry&lt;/a&gt; y &lt;a href="http://www.jboss.org/resteasy/"&gt;RESTEasy&lt;/a&gt; en la parte servidor.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo de RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Introducción y ejemplo de Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;Logging en Javascript con log4javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/capturar-errores-de-javascript.html"&gt;Capturar errores de Javascript&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/optimizar-modulos-de-requirejs.html"&gt;Optimizar módulos de RequireJS y archivos Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.genbetadev.com/desarrollo-web/patron-de-diseno-mvc-del-lado-cliente-con-backbonejs"&gt;Patrón de diseño MVC del lado cliente con Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://ejemplo%20de%20pruebas%20unitarias%20en%20javascript%20con%20jasmine%20y%20sinon/"&gt;Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/rbgZW88MqVM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/5546643635990216421/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5546643635990216421?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5546643635990216421?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/rbgZW88MqVM/introduccion-y-ejemplo-de-backbonejs.html" title="Introducción y ejemplo de Backbone.js" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-SMyoDvx_-j8/UXFvpfyC7iI/AAAAAAAACeY/uGQRcCderAg/s72-c/backbone.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QCSHY6fyp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7251162260480570051</id><published>2013-04-12T17:35:00.000+02:00</published><updated>2013-05-03T21:29:29.817+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:29:29.817+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Optimizar módulos de RequireJS y archivos Javascript</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img border="0" src="http://4.bp.blogspot.com/-M9RUvRUMUuY/UUOEKEfud5I/AAAAAAAACcY/mNZLmYzWnsU/s1600/requirejs.png" /&gt;&lt;/div&gt;
Una vez que tenemos desarrollada una &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;aplicación que hace uso de RequireJS&lt;/a&gt; como herramienta para gestionar las dependencias y archivos de javascript podemos realizar una optimización para conseguir que haya menos archivos y tengan menor tamaño. Esta optimización hará que la aplicación web realice menos peticiones al servidor y necesite transferir menos bytes dando como resultado una aplicación más rápida.&lt;br /&gt;
&lt;br /&gt;
Para conseguir esta optimización necesitamos tener instalado la herramienta node.js y descargar el javascript que realizará la optimización, &lt;a href="http://r.js/"&gt;r.js&lt;/a&gt;. En Arch Linux instalar &lt;a href="http://nodejs.org/"&gt;node.js&lt;/a&gt; consiste en instalar su paquete con:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5372631.js?file=script-1.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5372631/raw/script-1.sh"&gt;script-1.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Teniendo instalado node.js y habiendo descargado r.js el proceso consiste en ejecutar un comando desde la terminal. Este es:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5372631.js?file=script-2.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5372631/raw/script-2.sh"&gt;script-2.sh&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

El contenido del archivo build.js contendrá alguna información como el archivo de salida y la localización del módulo principal de la aplicación, básicamente la configuración es:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5372631.js?file=build.js"&gt;&lt;/script&gt;
&lt;noscript src="https://gist.github.com/picodotdev/5372631/raw/build.js"&gt;build.js&lt;/noscript&gt;


Este sería el resultado de optimizar el &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;ejemplo de introducción sobre RequireJS&lt;/a&gt;. En el resultado se han eliminado los espacios y tabuladores, los diferentes archivos se han fusionado en uno solo (en este caso únicamente figuras.js y main.js) y los nombres de las variables se han acortado, esto reduce el tamaño del Javascript final. Esta optimización sirve también en cierta forma como ofuscación del código si queremos dificultar a alguien (como a la competencia de un producto) se aproveche del código que hemos desarrollado:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5372631.js?file=built.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5372631/raw/built.js"&gt;built.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-2QAUlB8HZ2Q/UWgoBd8DlMI/AAAAAAAACeI/jIRqpALMy_Y/s1600/r.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="122" src="http://3.bp.blogspot.com/-2QAUlB8HZ2Q/UWgoBd8DlMI/AAAAAAAACeI/jIRqpALMy_Y/s320/r.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Al optimizar los archivos es recomendable ver las notas que indican en la &lt;a href="http://requirejs.org/docs/optimization.html"&gt;documentación de RequireJS&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Si se quiere optimizar archivos individuales de javascript o no usamos RequireJS se puede utilizar la herramienta&amp;nbsp;&lt;a href="http://closure-compiler.appspot.com/home"&gt;Closure Compiler&lt;/a&gt;, es muy sencilla, no hace falta instalar nada en nuestro equipo sino simplemente usar el servicio y da un resumen de la optimización que realiza.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo de RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Introducción y ejemplo de Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;Logging en Javascript con log4javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/capturar-errores-de-javascript.html"&gt;Capturar errores de Javascript&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Introducción y ejemplo de Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://ejemplo%20de%20pruebas%20unitarias%20en%20javascript%20con%20jasmine%20y%20sinon/"&gt;Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/92Uh9HuWLk0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7251162260480570051/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/04/optimizar-modulos-de-requirejs.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7251162260480570051?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7251162260480570051?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/92Uh9HuWLk0/optimizar-modulos-de-requirejs.html" title="Optimizar módulos de RequireJS y archivos Javascript" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-M9RUvRUMUuY/UUOEKEfud5I/AAAAAAAACcY/mNZLmYzWnsU/s72-c/requirejs.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/04/optimizar-modulos-de-requirejs.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QMSH48fSp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-5609663826000822153</id><published>2013-04-05T17:44:00.001+02:00</published><updated>2013-05-03T21:29:49.075+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:29:49.075+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Capturar errores de Javascript</title><content type="html">Tener un &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;sistema de logging para el código Javascript&lt;/a&gt; de una página está muy bien para depurar el código cuando las cosas van mal en el sentido de que no hacen o no se comportan como se espera. Pero a veces las cosas pueden ir peor que mal, esto es, si hay algún error de «compilación» en en código de javascript el navegador detiene la ejecución y por tanto dejan de emitirse las trazas. Si el error nos sucede en el entorno de desarrollo o pruebas en nuestro propio navegador nos daremos cuenta del error pero en un entorno de producción no ya que el código se ejecuta en el navegador del usuario.&lt;br /&gt;
&lt;br /&gt;
Sin embargo, no todo está perdido, los navegadores proporcionan como último recurso la función «window.onerror» que será llamada cuando se detecte un error de compilación o ejecución que impida continuar ejecutando el código javascript. Lo que podremos hacer para tratar el error será limitado pero al menos con log4javascript podremos enviar al servidor ese mensaje para que podamos primeramente conocer que se está produciendo y luego resolverlo, de otra manera nos sería desconocido ya que el código javascript se ejecuta en un entorno al que no tenemos acceso, en el navegador del usuario.&lt;br /&gt;
&lt;br /&gt;
En el siguiente ejemplo mostraré como enviar al servidor esos errores de javascript que se produzcan. Para conseguirlo usaremos una combinación de la librería log4javascript &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;comantada en la entrada anterior&lt;/a&gt; y la función window.onerror. A continuación el contenido de una página con el contenido completo usando &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5320271.js?file=HtmlError.html"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5320271/raw/HtmlError.html"&gt;HtmlError.html&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5320271.js?file=main.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5320271/raw/main.js"&gt;main.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En la siguiente imagen se puede ver la petición que hace log4javascript para enviar el error al servidor.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-HdDV3CfwIE4/UV7wUkDK9RI/AAAAAAAACd4/gpJaV0LazgU/s1600/javascript-error-log.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="73" src="http://3.bp.blogspot.com/-HdDV3CfwIE4/UV7wUkDK9RI/AAAAAAAACd4/gpJaV0LazgU/s320/javascript-error-log.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Aparte de la propia traza del error que envía log4javascript (aunque en la imagen anterior no es muy descriptiva) podemos obtener otra información relacionada con la petición http que se realiza como el navegador en el que se ha producido, sistema operativo y versiones de ambos aparte de cookies si las hubiese o la dirección IP. En esa URL deberemos tener algo que atienda la petición y saque a los logs del servidor el mensaje enviado. En caso de usar Java probablemente hagamos uso de un servlet, u otra cosa en función del framework web que usemos, y utilizaremos slf4j, log4j o logback para sacarlo al registro de trazas del servidor. El &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HtmlError"&gt;código completo de este ejemplo&lt;/a&gt; está alojado en mi repositorio de GitHub.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo de RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Introducción y ejemplo de Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;Logging en Javascript con log4javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/optimizar-modulos-de-requirejs.html"&gt;Optimizar módulos de RequireJS y archivos Javascript&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Introducción y ejemplo de Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://ejemplo%20de%20pruebas%20unitarias%20en%20javascript%20con%20jasmine%20y%20sinon/"&gt;Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/GTG4aG_lkgY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/5609663826000822153/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/04/capturar-errores-de-javascript.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5609663826000822153?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5609663826000822153?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/GTG4aG_lkgY/capturar-errores-de-javascript.html" title="Capturar errores de Javascript" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-HdDV3CfwIE4/UV7wUkDK9RI/AAAAAAAACd4/gpJaV0LazgU/s72-c/javascript-error-log.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/04/capturar-errores-de-javascript.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QNQng_eyp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7069472505121803597</id><published>2013-03-28T12:38:00.003+01:00</published><updated>2013-05-03T21:29:53.643+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:29:53.643+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Logging en Javascript con log4javascript</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="log4javascript" border="0" src="http://1.bp.blogspot.com/-lHJfQZVRCUg/UVQmPlNDywI/AAAAAAAACdQ/vqyWfQ-UWfA/s1600/log4javascript.png" title="log4javascript" /&gt;&lt;/div&gt;
En esta serie de artículos que estoy escribiendo sobre varias librerías y herramientas relacionadas con el lenguaje de programación Javascript como &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;RequireJS&lt;/a&gt;, &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Mustache&lt;/a&gt; y Backbone en esta ocasión voy a hablar de una librería de logging para javascript que también puede hacernos la vida más sencilla como programadores.&lt;br /&gt;
&lt;br /&gt;
Sistemas de trazas hay muchos para Java y otros muchos lenguajes pero en el entorno de las aplicaciones web estos suelen usarse en la parte del servidor de las aplicaciones. Sin embargo, en el cliente también puede producirse errores y nos puede interesar tener información para averiguar la causa de forma que podamos solucionarlos más rápidamente o simplemente para tener un registro de lo que se ha ido haciendo en este caso en el cliente. Una librería de logging en el cliente también nos puede servir para tener información de errores en el navegador que de otra forma nos pasarían desapercibidos si solo tenemos trazas en el servidor, con la librería de trazas que explicaré en esta entrada podemos enviar las trazas de error al servidor de forma muy sencilla. Además, nos puede servir de utilidad en el momento de desarrollo del código javascript.&lt;br /&gt;
&lt;br /&gt;
Por los motivos anteriores aunque no lo consideremos imprescindible nos puede resultar muy interesante la librería &lt;a href="http://log4javascript.org/"&gt;log4javascript&lt;/a&gt;. Los conceptos que emplea son muy parecidos a &lt;a href="http://logging.apache.org/log4j/1.2/"&gt;log4j&lt;/a&gt; con lo que si programamos en java aprenderemos fácil y rápidamente a usarla, en cualquier otro caso no nos llevará mucho tiempo aprender.&lt;br /&gt;
&lt;br /&gt;
Lo primero que deberemos hacer es descargarla y una vez descomprimido el paquete incluirla en el código html de nuestra página con, haciendo uso de RequireJS:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5262408.js?file=Log4javascript.html"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5262408/raw/Log4javascript.html"&gt;Log4javascript.html&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Posteriormente deberemos usarla definiendo los appenders que determinarán a donde se envían las trazas, a los appenders les asociaremos un layout que determinará el formato de la traza y el nivel mínimo que ha de tener la traza («threshold») para ser emitido. log4javascript proporciona varios appenders según nuestras preferencias o necesidades:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://log4javascript.org/docs/manual.html#alertappender"&gt;AlertAppender&lt;/a&gt;: muestra las trazas usando la función alert del navegador.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://log4javascript.org/docs/manual.html#ajaxappender"&gt;AjaxAppender&lt;/a&gt;: realiza una petición ajax al servidor enviando la traza generada en el navegador, con este appender podremos tener el registro de lo que sucede en los clientes. Se ha de indicar el path de la URL que procesará las trazas en el servidor.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://log4javascript.org/docs/manual.html#popupappender"&gt;PopUpAppender&lt;/a&gt;: muestra las trazas en una ventana emergente en la que se pueden filtrar las trazas y en la que cada nivel aparece con un color distinto para reconocerlas más fácilmente.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://log4javascript.org/docs/manual.html#inpageappender"&gt;InPageAppender&lt;/a&gt;: muestra la misma consola del PopUpAppender pero de forma incrustada en el pie de la página.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://log4javascript.org/docs/manual.html#browserconsoleappender"&gt;BrowserConsoleAppender&lt;/a&gt;: muestra los mensajes en la consola de depuración con los mensajes de javascript que incorporan los navegadores.&lt;/li&gt;
&lt;/ul&gt;
Con el siguiente bloque de código inicializamos el sistema de trazas, la variable log4j será la que utilicemos para emitir las trazas. En el ejemplo utilizamos el appender PopUpAppender con un layout que nos mostrará la hora, el nivel de la traza y el mensaje, el threshold del&amp;nbsp;PopUpAppender&amp;nbsp;es INFO con lo que todo mensaje con nivel menor prioridad como TRACE o DEBUG no aparecerán en el appender, para el caso de AjaxAppender el threshold será ERROR de modo que tampoco aparecerán en el las trazas INFO y WARN. Al emitir las trazas indicamos el mensaje más adecuado que consideremos más nos pueda ayudar y con el nivel de trazas según la importancia que tenga el mensaje, este nivel de trazas nos permitirá filtrarlos según su nivel en el popup:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5262408.js?file=main.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5262408/raw/main.js"&gt;main.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

El resultado que obtenemos en la consola emergente es el siguiente, los mensajes de nivel de trace y debug no aparecen por el threshold del appender PopupAppender.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-GATX44BETck/UVQomj_6oRI/AAAAAAAACdg/TXXDJmMCIKo/s1600/log4javascript-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="136" src="http://2.bp.blogspot.com/-GATX44BETck/UVQomj_6oRI/AAAAAAAACdg/TXXDJmMCIKo/s400/log4javascript-1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Como los ejemplos de RequireJS y Mustache el &lt;a href="https://github.com/picodotdev/elblogdepicodev"&gt;código fuente completo&lt;/a&gt; lo puedes encontrar en mi repositorio de GitHib.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo de RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Introducción y ejemplo de Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/capturar-errores-de-javascript.html"&gt;Capturar errores de Javascript&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/optimizar-modulos-de-requirejs.html"&gt;Optimizar módulos de RequireJS y archivos Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Introducción y ejemplo de Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://ejemplo%20de%20pruebas%20unitarias%20en%20javascript%20con%20jasmine%20y%20sinon/"&gt;Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/aoQbzgIWlcY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7069472505121803597/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/03/logging-en-javascript-con-log4javascript.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7069472505121803597?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7069472505121803597?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/aoQbzgIWlcY/logging-en-javascript-con-log4javascript.html" title="Logging en Javascript con log4javascript" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-lHJfQZVRCUg/UVQmPlNDywI/AAAAAAAACdQ/vqyWfQ-UWfA/s72-c/log4javascript.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/03/logging-en-javascript-con-log4javascript.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QNSXg6fSp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7779099654957752318</id><published>2013-03-22T18:04:00.002+01:00</published><updated>2013-05-03T21:29:58.615+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:29:58.615+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Introducción y ejemplo de Mustache</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;br /&gt;
&lt;img alt="Mustache" border="0" height="96" src="http://3.bp.blogspot.com/-c5az8LIB6ko/UUyJKk-lvYI/AAAAAAAACc0/B46DEgkNjh0/s200/mustache.png" title="Mustache" width="200" /&gt;&lt;/div&gt;
Como comentaba en la &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo sobre RequireJS&lt;/a&gt; el lado cliente están tomando cada vez más responsabilidad y peso en las aplicaciones. El lado cliente con los navegadores ya no se limitan a mostrar el html que se genera en el servidor de forma dinámica sino que son ellos mismos que obteniendo los datos en formato JSON probablemente mediante un servicio REST se encarga de transformar esos datos en el html que finalmente se ha de mostrar en la página. Generar el html en el cliente en vez de en el servidor tiene las siguientes ventajas:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Se reduce la carga del servidor.&lt;/li&gt;
&lt;li&gt;La cantidad de datos devueltos es menor ya que el tamaño de la plantilla y los datos podrán ser menor que el html final generado por la combinación de ambos si una plantilla puede aplicarse varias veces sobre alguna lista de los datos.&lt;/li&gt;
&lt;/ul&gt;
Para transformar esos datos al html mediante una plantilla hay diferentes librerías que proporcionan esa funcionalidad entre ellas &lt;a href="http://mustache.github.com/"&gt;Mustache&lt;/a&gt; y &lt;a href="http://handlebarsjs.com/"&gt;Handlebars&lt;/a&gt;. Mustache es más simple que Hadlebars y Handlebars es un superconjunto de Mustache por lo que si tenemos una plantilla en Mustache podremos utilizarla en Handlebars. En esta entrada comentaré un ejemplo simple y sencillo con las posibilidades de Mustache.&lt;br /&gt;
&lt;br /&gt;
Mustache se caracteriza por su simplicidad evitando incluir lógica en las plantillas, es decir, sin sentencias if y bucles aunque algunas etiquetas proporcionan la misma funcionalidad. El hecho de no incluir lógica en las plantillas hace que estas sean sencillas, fáciles de desarrollar y de mantener además de obligar a no mezclar conceptos lo que hace que el código sea «limpio».&lt;br /&gt;
&lt;br /&gt;
La esencia de Mustache al igual que otros sistemas de plantillas es que por un lado tenemos los datos que forman parte del modelo y por otro lado la plantilla que forma parte de la vista, juntando ambos obtenemos un resultado.&lt;br /&gt;
&lt;br /&gt;
Veamos como se traduce todo esto en código javascript. Lo primero y más sencillo son las variables que en la plantilla se indican entre {{ }}, estos corchetes con forma de mostacho le dan nombre a la librería, la variable es sustituida por el valor proporcionado en el modelo:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5222740.js?file=main-1.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5222740/raw/main-1.js"&gt;main-1.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
&lt;h4&gt;
Secciones&lt;/h4&gt;
Otra posibilidad de Mustache con las secciones que se indican con un # delante de la variable. Si la variable de una sección es false, tiene valor null o es un array vacio, esa sección se omite en el resultado. De esta forma una sección puede hacer de lo que en un lenguaje de programación sería una condición.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5222740.js?file=main-2.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5222740/raw/main-2.js"&gt;main-2.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
&lt;h4&gt;
Secciones lista&lt;/h4&gt;
En el caso de hacer una sección en el que la variable sea una array o lista, la sección recorrerá cada uno de los elementos de la lista y generará un contenido por cada elemento. Esto nos proporciona la funcionalidad de un bucle.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5222740.js?file=main-3.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5222740/raw/main-3.js"&gt;main-3.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
&lt;h4&gt;
Parciales&lt;/h4&gt;
Si no queremos tener una plantilla muy grande o queremos rehusar cierta parte en varias plantillas la podemos dividir usando parciales. Los parciales se indican con el caracter &amp;gt; y son una forma de llamar a un trozo de una plantilla. En la plantilla los parciales son sustituidos por su correspondiente código del mapa de parciales y la plantilla se procesa como si fuese una completa. Este es el mismo ejemplo de la sección lista pero usando parciales:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5222740.js?file=main-4.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5222740/raw/main-4.js"&gt;main-4.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
&lt;h4&gt;
Compilación&lt;/h4&gt;
Para una mayor eficiencia sobre todo en dispositivos móviles las plantillas pueden compilarse con:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5222740.js?file=main-5.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5222740/raw/main-5.js"&gt;main-5.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
Esto es lo básico de Mustache, tiene alguna posibilidad más como las funciones donde se puede añadir algo de lógica, también están las secciones invertidas que se indican con ^ y generan resultado si la variable de la sección es null, false o vacía, es decir, lo contrario de una sección normal. Por defecto los caracteres html de una variable son escapados pero puede evitarse con tres llaves {{{ }}} o un &amp;amp; antes del nombre de la variable. También puede cambiarse el delimitador de Muchache, los {{ }}, por otra cadena pero será poco habitual que lo necesitemos.&lt;br /&gt;
&lt;br /&gt;
La otra alternativa que comentaba es Handlebars con la que si tenemos una plantilla de Mustache podemos utilizarla en Handlebars sin ningún cambio ya que Handlebars soporta todas las características de Mustache y añade algunas otras. Las principales diferencias entre Mustache y Handlebars es que Mustache puede ser usada desde diferentes lenguajes como Java, Python, Ruby, D, .NET, PHP, y alguno más que puede verse en la página del proyecto, Handlebars solo se puede utilizar usando javascript. Dado que Mustache puede ser utilizado con Java puede ser una alternativa a librerías como &lt;a href="http://freemarker.sourceforge.net/"&gt;FreeMarker&lt;/a&gt; de esta manera podemos hacer uso de la misma librería de plantillas tanto en el servidor como en el cliente de forma que evitemos tener que aprender y conocer varias tecnologías que hacen lo mismo y aprovechemos mejor lo que conocemos. Otra diferencia es que Handlebars añade el concepto de funciones de ayuda («helpers») mediante los cuales se puede añadir lógica si nos es necesario y aunque puede facilitarnos ciertas tareas no conviene abusar de ello ya que puede hacer que las plantillas dejen de ser tan simples y empiecen a no ser fáciles de mantener.&lt;br /&gt;
&lt;br /&gt;
El &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HelloWorldMustache"&gt;código fuente completo de este ejemplo&lt;/a&gt; los puedes encontrar en mi repositorio de GitHub.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-requirejs.html"&gt;Introducción y ejemplo de RequireJS&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;Logging en Javascript con log4javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/capturar-errores-de-javascript.html"&gt;Capturar errores de Javascript&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/optimizar-modulos-de-requirejs.html"&gt;Optimizar módulos de RequireJS y archivos Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Introducción y ejemplo de Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://ejemplo%20de%20pruebas%20unitarias%20en%20javascript%20con%20jasmine%20y%20sinon/"&gt;Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/lEo7de5v_Gc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7779099654957752318/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/03/introduccion-y-ejemplo-de-mustache.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7779099654957752318?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7779099654957752318?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/lEo7de5v_Gc/introduccion-y-ejemplo-de-mustache.html" title="Introducción y ejemplo de Mustache" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-c5az8LIB6ko/UUyJKk-lvYI/AAAAAAAACc0/B46DEgkNjh0/s72-c/mustache.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/03/introduccion-y-ejemplo-de-mustache.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0MEQnYzcCp7ImA9WhBUFk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-179211981170632330</id><published>2013-03-15T22:18:00.003+01:00</published><updated>2013-05-03T21:30:03.888+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-03T21:30:03.888+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Introducción y ejemplo de RequireJS</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="RequireJS" border="0" src="http://4.bp.blogspot.com/-M9RUvRUMUuY/UUOEKEfud5I/AAAAAAAACcU/OYXvsfWUBbQ/s1600/requirejs.png" title="RequireJS" /&gt;&lt;/div&gt;
Las páginas web ha evolucionado mucho desde sus inicios en los que eran simples páginas estáticas hechas con el lenguaje de marcas html, podían contener enlaces e imágenes. Posteriormente adquirieron capacidad de cambiar a través un lenguaje de programación que en el servidor genera el código html de forma dinámica basándose en la información que el usuario podía enviar en un formulario, también se incorpora cierta programación en el cliente con javascript. Con las nuevas versiones del estándar html, los avances de los navegadores y una explosión de dispositivos móviles de gran capacidad las aplicaciones están evolucionando hacia el cliente, haciéndose cada vez más complejas en el lado del navegador del usuario y adquiriendo resposabilidades que antes tenía la aplicación en el lado del servidor. Cada vez hay más librerías y frameworks javascript que tratan de resolver problemas específicos de las aplicaciones de internet. Entre estas librerías algunas de las más conocidas son, muy resumidamente:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;: para manejar los elementos de la página.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/janl/mustache.js"&gt;mustache&lt;/a&gt;: a partir de una plantilla y unos datos genera un resultado.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://underscorejs.org/"&gt;underscore&lt;/a&gt;: proporciona ciertas utilidades bastante comunes que el lenguaje javascript no proporciona.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://backbonejs.org/"&gt;Backbone&lt;/a&gt;: da un modelo MVC para el desarollo de las aplicaciones.&lt;/li&gt;
&lt;/ul&gt;
Por supuesto, para cada área hay varias opciones entre las que se puede elegir, estas no son las únicas librerías hay muchas más alternativas (&lt;a href="http://handlebarsjs.com/"&gt;hadlebars&lt;/a&gt;, &lt;a href="http://prototypejs.org/"&gt;prototype&lt;/a&gt;, &lt;a href="http://angularjs.org/"&gt;agularjs&lt;/a&gt;, &lt;a href="http://mootools.net/"&gt;mootools&lt;/a&gt;, ...) que en esencia proporcionan la misma funcionalidad que las anteriores. A medida que vamos haciendo uso de más librerías, archivos javascript y que estas pueden tener dependencias unas sobre otras se hace necesario algo que permita gestionar esas relaciones entre las librerías para que el código javascript se cargue en el orden adecuado y funcione correctamente. Aquí surge &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt;, que además de gestionar esas dependencias también nos proporciona otras ventajas:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;La carga de los archivos javascript se hace de forma asíncrona evitando el resto del contenido de la página se bloque hasta que los js de la página se carguen.&lt;/li&gt;
&lt;li&gt;Permitirnos optimizar los archivos javascript juntándolos en uno solo de forma que sean necesarias menos peticiones al servidor y a la vez minificándolos para hacer que ocupen menos y la transferencia sea más rápida.&lt;/li&gt;
&lt;li&gt;Se evita contaminar el ámbito global de javascript evitando posibles conflictos entre archivos javascript.&lt;/li&gt;
&lt;/ul&gt;
Algunas de estas ventajas hacen que la página cargue más rápido que es algo que el buscador de Google tiene muy en cuenta para el posicionamiento en los resultados de búsqueda.&lt;br /&gt;
&lt;br /&gt;
Con RequireJS los archivos javascript se organizan en módulos y estos pueden tener dependencias sobre otros, todos los archivos javascript necesarios son cargados por RequireJS de forma asíncrona. Haciendo uso de RequireJS en la página web solo será necesario incluir un único javascript, que será el de RequireJS, en la propia etiqueta script y con el atributo data-main le indicaremos el módulo del que va a hacer uso la página. El resto de módulos se cargarán como dependencias del módulo que especifiquemos en el atributo data-main.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5172996.js?file=HelloWorldRequireJS.html"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5172996/raw/HelloWorldRequireJS.html"&gt;HelloWorldRequireJS.html&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En el módulo inicial que carga la página, main.js, hacemos uso de la función require cuyos parámetros son un array con la lista de dependencias sobre otros módulos y una función que será llamada cuando RequireJS haya cargado todos los módulos indicados en la lista, en este caso solo uno. Por cada dependencia la función que se llama cuando todos los módulos se han cargado recibe un objeto como parámetro, de esta manera RequireJS es como evita contaminar el ámbito global ya que no son necesarias definiciones en el ámbito global.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5172996.js?file=main.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5172996/raw/main.js"&gt;main.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En el ejemplo del módulo main.js en la función require se indica una dependencia sobre el módulo figuras. El módulo de las figuras contiene la lógica de esta simple aplicación que es calcular el área de un cuadrado y un triángulo y mostrar el resultado en un diálogo emergente. Una vez que todas las dependencias del módulo de la aplicación, en este caso solo una, han sido cargadas por RequireJS se llama a la función callback del módulo de la aplicación con la referencia del módulo de figuras.&lt;br /&gt;
&lt;br /&gt;
El módulo figuras no tiene ninguna dependencia con lo que el array de dependencias no es necesario y solo se indica la función de inicialización del módulo en la función define. Este módulo define los prototipos de dos objetos, un cuadrado y un triángulo, con un método cada una de ellas que permiten calcular su respectiva área. El retorno de la función de inicialización del módulo es importante ya que lo devuelto por esta función es lo que recibirán como referencia los módulos que dependan sobre el módulo de figuras.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5172996.js?file=figuras.js"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5172996/raw/figuras.js"&gt;figuras.js&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

El resultado es este:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-pZVw16xEz8o/UUOLde7g6KI/AAAAAAAACcc/f2GVpba2lDc/s1600/requirejs-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="138" src="http://4.bp.blogspot.com/-pZVw16xEz8o/UUOLde7g6KI/AAAAAAAACcc/f2GVpba2lDc/s200/requirejs-2.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-J5Dxevmlkfs/UUOLdXPk_nI/AAAAAAAACck/1qADmcHKDQw/s1600/requirejs-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="138" src="http://1.bp.blogspot.com/-J5Dxevmlkfs/UUOLdXPk_nI/AAAAAAAACck/1qADmcHKDQw/s200/requirejs-1.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Las funciones require y define son similares pero si un módulo va a ser usado por otros se ha de usar la función define, sino va a ser usado por otros se puede usar la función require.&lt;br /&gt;
&lt;br /&gt;
En posteriores entradas hablaré y pondré un ejemplo de otros temas relacionados con javascript como mustache, backbone, REST, logging, optimización, ... que nos pueden ayudar mucho a hacer mejores aplicaciones, más complejas, de forma más sencilla, con menos errores y más rápidamente. Este artículo sobre RequireJS solo es el primero en el que se basarán el resto que escribiré sobre javascript.&lt;br /&gt;
&lt;br /&gt;
El &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HelloWorldRequireJS"&gt;código fuente completo de este ejemplo de RequireJS&lt;/a&gt; lo puedes encontrar en mi repositorio de GitHub.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/introduccion-y-ejemplo-de-mustache.html"&gt;Introducción y ejemplo de Mustache&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/logging-en-javascript-con-log4javascript.html"&gt;Logging en Javascript con log4javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/capturar-errores-de-javascript.html"&gt;Capturar errores de Javascript&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/optimizar-modulos-de-requirejs.html"&gt;Optimizar módulos de RequireJS y archivos Javascript&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2013/04/introduccion-y-ejemplo-de-backbonejs.html"&gt;Introducción y ejemplo de Backbone.js&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/04/ejemplo-lista-de-tareas-con-backbone.html"&gt;Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://ejemplo%20de%20pruebas%20unitarias%20en%20javascript%20con%20jasmine%20y%20sinon/"&gt;Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon&lt;/a&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/R_5vzsETzIE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/179211981170632330/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/03/introduccion-y-ejemplo-de-requirejs.html#comment-form" title="4 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/179211981170632330?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/179211981170632330?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/R_5vzsETzIE/introduccion-y-ejemplo-de-requirejs.html" title="Introducción y ejemplo de RequireJS" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-M9RUvRUMUuY/UUOEKEfud5I/AAAAAAAACcU/OYXvsfWUBbQ/s72-c/requirejs.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/03/introduccion-y-ejemplo-de-requirejs.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYGRX8zeyp7ImA9WhBRGU8.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1141023566204052196</id><published>2013-03-09T12:30:00.000+01:00</published><updated>2013-03-10T13:55:24.183+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-10T13:55:24.183+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Skinning de web usando Apache Tapestry</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apache Tapestry" border="0" height="133" src="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s320/tapestry.png" title="Apache Tapestry" width="320" /&gt;&lt;/div&gt;
Algunas páginas o aplicaciones web tienen la necesidad de variar su aspecto en función de determinadas variables. La aplicación puede ser la misma pero necesitar cambiar el aspecto o la imagen de marca en función de esas variables. Si los cambios a realizar solo implican cosas visuales como colores, tipos de fuentes y tamaños, imágenes, etc... con cambiar las hojas de estilo de la web puede ser suficiente, pero si es necesario modificar el html que genera la aplicación porque los elementos sean diferentes o estén en diferente posición dentro del html generado necesitaremos incluir lógica en forma de condiciones en las plantillas &amp;nbsp;que forman parte de la vista del framework que usemos. Si únicamente tenemos una variable incluir lógica en las plantillas puede ser manejable pero si son varias tarde tarde o temprano su mantenimiento será un problema y podremos tener como resultado &lt;a href="http://es.wikipedia.org/wiki/C%C3%B3digo_spaghetti"&gt;código spaghetti&lt;/a&gt; en el html. Otra alternativa a usar una única plantilla con lógica es tener una plantilla distinta por variable o conjunto de variables si las plantillas generan código html suficientemente diferente de forma que manejar las diferentes plantillas por separado sea más sencillo, a pesar de que probablemente estaremos duplicando código en las diferentes plantillas. Pero para tener diferentes plantillas tendremos que inventar un mecanismo que nos permita seleccionar la plantilla adecuada en función de las variables y las cosas pueden complicarse si se puede dar combinaciones de ellas.&lt;br /&gt;
&lt;br /&gt;
¿Cuales podrían ser las variables por las que una aplicación quisiese cambiar el html generado? Algunas de ellas podrían ser:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;El nombre del dominio. Si aplicación necesita de diferente imagen de marca en función del nombre de dominio por el que se accede al servidor donde está alojada la aplicación.&lt;/li&gt;
&lt;li&gt;El tipo de usuario. Aquí podría entrar cualquier información que el usuario nos envía en las peticiones http, desde el lenguaje al país de la dirección IP pero una candidata podría ser el dispositivo utilizado para acceder a la aplicación, si se tratase de un dispositivo móvil podríamos eliminar las cosas suplerfuas para aligerar en peso la página y que se cargase más rápido o para que esté más adaptada a un ambiente táctil de un table o smartphone.&lt;/li&gt;
&lt;li&gt;Otra podría ser la hora o fecha en la que se accede a la aplicación por ejemplo para adaptar la aplicación a épocas navideñas o ciertos periodos relevantes para la aplicación.&lt;/li&gt;
&lt;/ul&gt;
Si tenemos una aplicación con esta problemática deberemos buscar la mejor forma que el framework que usemos nos posibilite. A continuación voy a explicar como conseguirlo usando el framework &lt;a href="http://tapestry.apache.org/"&gt;Apache Tapestry&lt;/a&gt;. Usando este framework veremos que añadiendo una pocas cosas podemos resolver el problema de forma simple, sencilla y limpia evitando tener la misma lógica desperdiga por toda la aplicación. Una vez realizada la selección de la plantilla adecuada su uso nos será transparente y nos podremos olvidar de esta funcionalidad y centrarnos únicamente en crear las plantillas.&lt;br /&gt;
&lt;br /&gt;
En Tapestry a las variables se les denomina «axis» o ejes, una de ellas disponible por defecto y especial es el «locale» o idioma del usuario, de esta forma en función del idioma podemos tener una plantilla distinta y completamente diferente del resto, si no existe la más específica se usará la mejor opción al igual que ocurre con los &lt;a href="http://docs.oracle.com/javase/7/docs/api/java/util/ResourceBundle.html"&gt;ResourceBundle&lt;/a&gt; de Java. Por ejemplo, si el idioma es es_ES puede ocurrir los siguientes casos:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Que la plantilla de una página o componente con eje del idioma es_ES exista, si la página es Index, su plantilla es Index_es_ES.tml si existe se selecciona esta.&lt;/li&gt;
&lt;li&gt;Si la plantilla Index_es_ES.tml no existe se elige la siguiente mejor opción que es Index_es.tml.&lt;/li&gt;
&lt;li&gt;Si Index_es.tml tampoco existe se selecciona Index.tml que será la más general y opción de último recurso.&lt;/li&gt;
&lt;/ul&gt;
En Tapestry el eje del idioma se aplica también a los recursos estáticos como puede ser las imágenes, css, javascripts, etc... en el caso de las imágenes nos puede ser muy útil si estas llevan texto. Los ejes personalizados fueron introducidos en la versión de Tapestry 5.3, aunque ya era posible conseguirlos en versiones anteriores se necesitaba más esfuerzo. Podemos tener tantos ejes como deseemos pero si son muchos las combinaciones y plantillas necesarias pueden crecer en gran medida aunque al igual con el eje del «locale» si la plantilla no existe se seleccionará la de por defecto, por suerte no será necesario que definamos todas las plantillas para todos los posibles casos de combinaciones de ejes solo deberemos crear las plantillas personalizadas que nos interesen. Las combinaciones de los ejes y las plantillas puede observarse en la siguientes figuras.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-VvkO6b-1kLA/UTsTniFUCYI/AAAAAAAACbU/dw6mGFSldcA/s1600/tapestry-axis-locale.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="163" src="http://2.bp.blogspot.com/-VvkO6b-1kLA/UTsTniFUCYI/AAAAAAAACbU/dw6mGFSldcA/s200/tapestry-axis-locale.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-iNeSXDh5290/UTsTnpvIVsI/AAAAAAAACbY/BhyjygL-dzA/s1600/tapestry-axis-multiple.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="188" src="http://4.bp.blogspot.com/-iNeSXDh5290/UTsTnpvIVsI/AAAAAAAACbY/BhyjygL-dzA/s200/tapestry-axis-multiple.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Quizá todo parezca más complicado de lo que realmente es, la implementación en código de los ejes se traduce básicamente en dos partes:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;La lógica que determina los ejes a aplicar a una petición.&lt;/li&gt;
&lt;li&gt;La lógica que en función de los ejes de la petición selecciona la plantilla adecuada.&lt;/li&gt;
&lt;/ul&gt;
Una de las grandes características de Tapestry es que las cosas pueden ser redefinidas, extendidas y adaptadas, todo lo que esté definido como un servicio en el contenedor de dependencias puede serlo y esto es mucha de la funcionalidad interna del framework ya que muchas cosas se definen como servicios, solo deberemos implementar la interfaz adecuada en una clase y hacer la contribución en el contenedor de dependencias para que se use.&lt;br /&gt;
&lt;br /&gt;
Veamos la clase que contiene la lógica que determina el eje a aplicar en función del dominio suponiendo que la plantilla a usar sea diferente si se entra a la aplicación por el dominio .com.es o por .net, la interfaz a implementar es &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/pageload/ComponentRequestSelectorAnalyzer.html"&gt;ComponentRequestSelectorAnalyzer&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5123842.js?file=CustomComponentRequestSelectorAnalyzer.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5123842/raw/CustomComponentRequestSelectorAnalyzer.java"&gt;CustomComponentRequestSelectorAnalyzer.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
La clase CustomComponentRequestSelectorAnalyzer para determinar si la aplicación cumple un eje necesitará información, en el caso del eje del dominio bastará con la clase &lt;a href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html"&gt;HttpServletRequest&lt;/a&gt; que podrá ser inyectada por el contenedor IoC de Tapestry en la implementación de la clase, si necesitásemos más información será suficiente con indicar el servicio que la proporciona en el constructor. DominioAxis es un enumerado de utilidad para identificar el dominio. Si el nombre del dominio acaba en com.es o .net devuelve el enum correspondiente:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5123842.js?file=DominioAxis.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5123842/raw/DominioAxis.java"&gt;DominioAxis.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
La segunda parte de los ejes es proporcionada por una clase que implementa la interfaz &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/pageload/ComponentResourceLocator.html"&gt;ComponentResourceLocator&lt;/a&gt; y seleccionará la plantilla adecuada en función de los ejes detectados en la petición por la clase CustomComponentRequestSelectorAnalyzer anterior. La parte importante está en el método &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/pageload/ComponentResourceLocator.html#locateTemplate(org.apache.tapestry5.model.ComponentModel, org.apache.tapestry5.services.pageload.ComponentResourceSelector)"&gt;locateTemplate&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5123842.js?file=CustomComponentResourceLocator.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5123842/raw/CustomComponentResourceLocator.java"&gt;CustomComponentResourceLocator.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
Se obtienen los ejes por los que pueden seleccionar las plantillas, en este caso solo uno pero podrían ser múltiples y combinados de cualquier forma aunque en ese caso la lógica para selecciona la plantilla sería más complicada. Independientemente del número de ejes el resultado final ha de ser el nombre de la plantilla y si existe se usa sino se usa la plantilla que no tiene ningún eje (salvo el locale que se puede aplicar siempre incluso usando ejes personalizados). En el caso concreto del ejemplo la plantilla sigue el formato «[nombre plantilla]_[eje dominio].tml» que en el caso de la página Index podría ser Index_com_es.tml o Index_net.tml. Si la petición no tuviese ningún eje la decisión de seleccionar la plantilla se delegaría en el servicio por defecto de Tapestry.&lt;br /&gt;
&lt;br /&gt;
Una vez codificadas estas clases para que se usen se han de hacer unas pocas contribuciones en el módulo de la aplicación, en concreto lo que necesitamos definir son dos métodos, contributeServiceOverride y decorateComponentResourceLocator. Si los nombres de estos métodos no son suficientemente explicativos diré que contributeServiceOverride hace que se use un servicio en vez de otro, como queremos que se use nuestro CustomComponentRequestSelectorAnalyzer en vez del de por defecto que usa Tapestry lo sobreescribimos, nuestro servicio lo definimos en el método bind. El método decorateComponentResourceLocator aplica el patrón de diseño &lt;a href="http://es.wikipedia.org/wiki/Decorator_(patr%C3%B3n_de_dise%C3%B1o)"&gt;Decorator&lt;/a&gt; sobre otro servicio, si la clase CustomComponentResourceLocator detecta un eje y encuentra la plantilla la selecciona sino siguiendo el patrón decorate delega en la clase que decora.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5123842.js?file=AppModule.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5123842/raw/AppModule.java"&gt;AppModule.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;
&lt;br /&gt;
Y este sería el resultado de acceder por los diferentes dominios:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-hNHjLXbMMa0/UTsY3F36A6I/AAAAAAAACbk/duJpzHFxs1I/s1600/tapestry-skinning-localhost-net.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="131" src="http://2.bp.blogspot.com/-hNHjLXbMMa0/UTsY3F36A6I/AAAAAAAACbk/duJpzHFxs1I/s200/tapestry-skinning-localhost-net.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-AINbkthj0_I/UTsY3PUGVoI/AAAAAAAACbo/HIiFvLu1SVI/s1600/tapestry-skinning-localhost-com-es.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="131" src="http://2.bp.blogspot.com/-AINbkthj0_I/UTsY3PUGVoI/AAAAAAAACbo/HIiFvLu1SVI/s200/tapestry-skinning-localhost-com-es.png" width="200" /&gt;&lt;/a&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-UzskQ6HwOM0/UTsY3KWvkJI/AAAAAAAACbw/1GoU3nnXcwg/s1600/tapestry-skinning-localhost-local.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="131" src="http://2.bp.blogspot.com/-UzskQ6HwOM0/UTsY3KWvkJI/AAAAAAAACbw/1GoU3nnXcwg/s200/tapestry-skinning-localhost-local.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Estas son las plantillas que se usan en cada uno de los casos:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;En el caso del dominio que acaba en .local se usan las plantillas de la página Index.tml y HolaMundo.tml del componente HolaMundo ya que CustomComponentRequestSelectorAnalyzer no establece el eje del dominio.&lt;/li&gt;
&lt;li&gt;En el dominio .com.es si se establece el eje de dominio y se buscan las plantillas de este eje, en el caso de la plantilla de la página se Index.tml ya que no existe Index_com_es.html pero en el caso del componente se usa HolaMundo_com_es.tml ya que si existe.&lt;/li&gt;
&lt;li&gt;En el dominio .net se usa Index_net.tml y HolaMundo_net.tml ya que en ambos casos existen.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-hY0paF6jKYQ/UTyCo_PluTI/AAAAAAAACcE/nxr3thYjspI/s1600/tapestry-skinning-recursos.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="257" src="http://1.bp.blogspot.com/-hY0paF6jKYQ/UTyCo_PluTI/AAAAAAAACcE/nxr3thYjspI/s320/tapestry-skinning-recursos.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
El &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestrySkinning"&gt;código fuente completo&lt;/a&gt; lo puedes encontrar en mi repositorio de GitHub. ¿Quieres saber más sobre Apache Tapestry? Visita&amp;nbsp;&lt;a href="http://elblogdepicodev.blogspot.com.es/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://blog.tapestry5.de/index.php/2011/06/24/template-skinning/"&gt;http://blog.tapestry5.de/index.php/2011/06/24/template-skinning/&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestrySkinning"&gt;Código fuente completo del ejemplo Skinning de plantillas con Apache Tapestry &lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/ulO6ZbrrovE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1141023566204052196/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/03/skinning-de-web-usando-apache-tapestry.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1141023566204052196?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1141023566204052196?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/ulO6ZbrrovE/skinning-de-web-usando-apache-tapestry.html" title="Skinning de web usando Apache Tapestry" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s72-c/tapestry.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/03/skinning-de-web-usando-apache-tapestry.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYEQH86eSp7ImA9WhBREUo.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-5796310947580360285</id><published>2013-03-01T20:23:00.001+01:00</published><updated>2013-03-01T23:15:01.111+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-01T23:15:01.111+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Integración de Apache Tapestry con RESTEasy</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="RESTEasy" border="0" height="141" src="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s200/resteasy.png" title="RESTEasy" width="200" /&gt;&lt;/div&gt;
Ahora que hemos visto en artículos anteriores como son los servicios web &lt;a href="http://es.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt;, &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html"&gt;como realizar uno empleando la librería RESTeasy&lt;/a&gt;, &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/cliente-javascript-y-java-de-servicio-web-resteasy.html"&gt;como crear un cliente para consumirlo desde el lenguaje de programación java o mediante javascript desde un navegador web&lt;/a&gt; y &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/devolver-xml-json-o-html-con-resteasy.html"&gt;como devolver datos en formato json, xml u otro&lt;/a&gt; se nos plantean algunas ideas de como definir la arquitectura de una aplicación.&lt;br /&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apache Tapestry 5" border="0" height="132" src="http://4.bp.blogspot.com/-OGvCbjBk0zU/TOq7Y6w6wfI/AAAAAAAAAiU/Pfuzt5kkDBQ/s320/tapestry.png" title="Apache Tapestry 5" width="320" /&gt;&lt;/div&gt;
&lt;br /&gt;
Las aplicaciones web normalmente se organizan en tres capas, la del cliente, la que contiene la lógica de negocio y la capa de base de datos. La capa del cliente está formada por el navegador, la de negocio donde se emplea algún framework y variará según lenguaje de programación que empleemos e incluye a menudo la responsabilidad de presentación transformando los datos de la capa de base de datos en html para el cliente. La capa de base de datos contiene los datos que trata la aplicación ya sea en forma relacional o no relacionan (no-sql).&lt;br /&gt;
&lt;br /&gt;
En algunas aplicaciones es requisito ofrecer una interfaz de la aplicación para que otras aplicaciones se integren con ella, aqui es donde surgen los servicios web y se nos platea la posibilidad de extraer de la capa donde esta la lógica de negocio a una entidad independiente o integrar los servicios web como una parte más de la aplicación web.&lt;br /&gt;
&lt;br /&gt;
En los ejemplos que hemos visto hasta ahora estos los he puesto como una entidad independiente, es decir, que solo ofrece los servicios web. Esta forma tiene la ventaja de que las partes de la aplicación son independientes pero tiene la desventaja de que la complejidad de la aplicación aumenta ya que los servicios web necesitarán de funcionalidades como acceder a la base de datos, inyección de dependencias o de la recarga automática de clases para ver las modificaciones en caliente mientras estamos desarrollando. Todas estas funcionalidades es probable que tengamos ya en la capa de lógica de negocio, con lo que podríamos ponerlo todo junto y aprovecharnos de las funcionadades que ya tenemos en vez de proporcionalas de nuevo en otra entidad independiente. Sin embargo, para tenerlo todo junto deberemos integrar el framework que usermos con la librería o framework que usemos para la aplicación web. En el siguiente ejemplo veremos como hacerlo para el caso de &lt;a href="http://www.jboss.org/resteasy/"&gt;RESTEasy&lt;/a&gt; como librería que proporciona la funcionalidad de servicios web y &lt;a href="http://tapestry.apache.org/"&gt;Apache Tapestry&lt;/a&gt;&amp;nbsp;como framework para la aplicación web desarrollada en Java.&lt;br /&gt;
&lt;br /&gt;
En Tapestry ya hay una &lt;a href="http://tynamo.org/tapestry-resteasy+guide"&gt;librería desarollada que permite integrarse con RESTEasy&lt;/a&gt;, sin embargo, por lo que he visto en &lt;a href="http://svn.codehaus.org/tynamo/trunk/tapestry-resteasy/"&gt;su código fuente&lt;/a&gt; no permite obtener el cliente javascript como explique en el &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/cliente-javascript-y-java-de-servicio-web-resteasy.html"&gt;segundo ejemplo&lt;/a&gt; de esta serie de entradas sobre RESTEasy. Con lo que partiendo de su código fuente lo he modificado para que si se permita obtenerlo.&lt;br /&gt;
&lt;br /&gt;
La parte importante de esta integración está en las clases que definene el módulo de la aplicación (AppModule.java) y el filtro de RESTEasy (ResteasyRequestFilter.java) que será el que atrape las peticiones REST. El código está comentado con notas explicativas.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5066888.js?file=AppModule.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5066888/raw/AppModule.java"&gt;AppModule.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5066888.js?file=ResteasyRequestFilter.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5066888/raw/ResteasyRequestFilter.java"&gt;ResteasyRequestFilter.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Con el código de AppModule.java y ResteasyRequestFilter.java conseguimos la integración de Tapestry con RESTEasy y con ella conseguimos varias características que proporciona el &lt;a href="http://tapestry.apache.org/ioc.html"&gt;contenedor IoC&lt;/a&gt; (&lt;a href="http://en.wikipedia.org/wiki/Inversion_of_control"&gt;Inversion of Control&lt;/a&gt;), el mero hecho de definir los servicios REST como servicios de Tapestry obtenemos inyección de dependencias y recarga de clases en vivo («&lt;a href="http://tapestry.apache.org/class-reloading.html"&gt;live class reloading&lt;/a&gt;») que evitará que durante el desarrollo tengamos que estar constantemente parando y arrancando el servidor de aplicaciones para ver los cambios que hagamos con nuestro editor a la clase del servicio, ambas cosas sin que tengamos que hacer nada más. En cuanto a la inyección de dependencias si nos fijamos en el código de servicio REST&amp;nbsp;HelloWorldResourceImpl vemos que tiene un constructor con una referencia a un objeto de la interfaz&amp;nbsp;&lt;a href="https://github.com/picodotdev/elblogdepicodev/blob/master/TapestryRESTEasy/src/main/java/es/com/blogspot/elblogdepicodev/tapestry/resteasy/services/ContadorService.java"&gt;ContadorService&lt;/a&gt;, sin esta referencia el objeto no puede ser construido. El contenedor de dependencias de Tapestry es capaz de inyectar las dependencias necesarias en el constructor para satisfacer sus necesidades sin que tengamos que darle ninguna indicación, él se encargará de buscar en el registro un servicio que implemente esa interfaz, de construirlo (y de construir las dependencias de este servicio si a su vez las tuviese) y de pasarlo como referencia al constructor, encontrará que la clase ContadorServiceImpl lo implementa ya que así lo hemos indicado en el método AppModule.bind. De esta misma forma en los servicios REST podemos hacer uso de cualquier otro servicio, como pudiera ser el &lt;a href="http://tapestry.apache.org/integrating-with-jpa.html"&gt;servicio persistencia&lt;/a&gt; proporcionado por Hibernate o JPA.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5066888.js?file=HelloWorldResourceImpl.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5066888/raw/HelloWorldResourceImpl.java"&gt;HelloWorldResourceImpl.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

La página que genera el html (Index.tml) para el navegador con varias llamadas al servicio REST (Index.tml) es prácticamente html normal excepto por un ejemplo de como se usa un componente en Tapestry (&lt;t:holamundo&gt;). La etiqueta script solicita el javascript con el cliente del servicio web.&lt;/t:holamundo&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5066888.js?file=Index.tml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5066888/raw/Index.tml"&gt;Index.tml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Y las peticiones que veríamos en el navegador serían:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-nTZHAW6xPLQ/UTD8HiEad6I/AAAAAAAACag/S5BPSAogxZ0/s1600/apache-tapestry-resteasy-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="125" src="http://2.bp.blogspot.com/-nTZHAW6xPLQ/UTD8HiEad6I/AAAAAAAACag/S5BPSAogxZ0/s200/apache-tapestry-resteasy-1.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-wYn2-0kC7Vw/UTD8Hh3scCI/AAAAAAAACac/fi9vxhgSZPU/s1600/apache-tapestry-resteasy-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="125" src="http://1.bp.blogspot.com/-wYn2-0kC7Vw/UTD8Hh3scCI/AAAAAAAACac/fi9vxhgSZPU/s200/apache-tapestry-resteasy-2.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
Puedes ver, descargar y probar el &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestryRESTEasy"&gt;código fuente del ejemplo&lt;/a&gt; desde mi repositorio de GitHub.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://es.wikipedia.org/wiki/Representational_State_Transfer"&gt;http://es.wikipedia.org/wiki/Representational_State_Transfer&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/TapestryRESTEasy"&gt;Código fuente ejemplo&amp;nbsp;Integración de Apache Tapestry con RESTEasy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/qHNMOwrM_Wk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/5796310947580360285/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/03/integracion-tapestry-con-resteasy.htm.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5796310947580360285?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5796310947580360285?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/qHNMOwrM_Wk/integracion-tapestry-con-resteasy.htm.html" title="Integración de Apache Tapestry con RESTEasy" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s72-c/resteasy.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/03/integracion-tapestry-con-resteasy.htm.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcGRXkzeSp7ImA9WhBREUo.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1018335039820031572</id><published>2013-02-22T18:36:00.000+01:00</published><updated>2013-03-01T23:30:24.781+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-01T23:30:24.781+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Devolver xml, json o html con RESTEasy</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="RESTEasy" border="0" src="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s1600/resteasy.png" title="RESTEasy" /&gt;&lt;/div&gt;
En esta serie de artículos sobre los &lt;a href="http://es.wikipedia.org/wiki/Representational_State_Transfer"&gt;servicios web REST&lt;/a&gt; hemos visto hasta el momento como hacer un &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html"&gt;programa sencillo Hola Mundo con RESTEasy&lt;/a&gt; y &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/cliente-javascript-y-java-de-servicio-web-resteasy.html"&gt;como crear un cliente Java y Javascript de ese servicio web&lt;/a&gt; para consumirlo desde javascript en una aplicación web y desde un programa java usando la librería &lt;a href="http://www.jboss.org/resteasy/"&gt;RESTEasy&lt;/a&gt;. En un servicio web REST podemos devolver datos en varios formatos, ya sea texto plano, html, json o xml, en esta entrada veremos como devolver datos en los dos últimos y que anotaciones usar para que las cabeceras devueltas en la petición sean las correctas, que ventajas nos puede aporta y que problemas nos puede resolver.&lt;br /&gt;
&lt;br /&gt;
Antes unos pequeños apuntes de porque nos puede interesar devolver json o xml. Unos de los motivos es que a medida que las aplicaciones web están ganando en complejidad estas están tendiendo a delegar en el cliente la parte de visualización en vez de ser el servidor el que devuelva html formateado directamente. Además de evitar cierta carga de proceso en el servidor y delegarla en los clientes ofreciendo una interfaz REST de la apliccación permitimos que terceros desarrollen sus propias aplicaciones y se integren consumiendo los servicios que ofrecemos.&lt;br /&gt;
&lt;br /&gt;
Una vez explicadas algunas ventajas, veamos el ejemplo de como devolver json o xml en el servicio REST, en el siguiente código el servicio HelloWorldResource devolverá los datos de un objeto a través de los métodos getMensajeJSON y getMensajeXML. Los métodos del servicio puede anotarse con @Produces indicando el formato de los datos que se devuelve, en la clase &lt;a href="http://docs.jboss.org/resteasy/docs/2.3.5.Final/javadocs/javax/ws/rs/core/MediaType.html"&gt;MediaType&lt;/a&gt; hay constantes para muchos formatos. Si el formato en el que devolvemos los datos no está definido en la clase MediaType podemos definir uno específico con @Produces("text/html"):&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5014791.js?file=HelloWorldResource.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5014791/raw/HelloWorldResource.java"&gt;HelloWorldResource.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;script src="https://gist.github.com/picodotdev/5014791.js?file=HelloWorldResourceImpl.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5014791/raw/HelloWorldResourceImpl.java"&gt;HelloWorldResourceImpl.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

La anotación @Produces es muy importante en el servicio REST ya que los métodos getMensajeJSON y getMensajeXML tienen especificada la misma ruta de petición en la etiqueta @Path, ¿como sabe entonces el servicio REST en que formato se han devolver los datos si la URL solicitada es la misma para ambos casos? La respuesta es empleando la información enviada en las cabeceras del protocolo http, más concretamente empleando la cabecera Accept que en este protocolo indica en que formato quiere el cliente los datos. En muchas aplicaciones es habitual encontrarse en que se realizan peticiones al servidor especificando como Accept text/html y resulta que el servidor devuelve los datos en formato json, xml u otro, la aplicación funcionará bien pero lo hace empleando el protocolo http de forma incorrecta, esto no ocurre con los servicios REST que en función de lo que se solicita en la petición así es el formato de los datos devueltos. En la siguiente imagen pude verse la cabecera Accept de la petición enviada por el cliente javascript.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-YIctwFeTat0/USlEU72EuyI/AAAAAAAACZI/8FLBG04qHTg/s1600/resteasy-cabeceras.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="98" src="http://1.bp.blogspot.com/-YIctwFeTat0/USlEU72EuyI/AAAAAAAACZI/8FLBG04qHTg/s320/resteasy-cabeceras.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
En el ejemplo se devuelve una clase POJO con varios atributos que usa las anotaciones @XmlRootElement, @XmlElement, @XmlAttribute para indicar los datos y la forma de los datos a generar como resultado:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5014791.js?file=Mensaje.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5014791/raw/Mensaje.java"&gt;Mensaje.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Para el cliente java desde el punto de vista del que lo usa la comunicación con el servidor y el formato en el que se transmiten los mensajes es transparente.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5014791.js?file=HelloWorldResourceClient.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5014791/raw/HelloWorldResourceClient.java"&gt;HelloWorldResourceClient.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-V16FRw94ofo/USeqiiz_TKI/AAAAAAAACX0/dz2VCU3GPzQ/s1600/resteasy-cliente-java.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="124" src="http://1.bp.blogspot.com/-V16FRw94ofo/USeqiiz_TKI/AAAAAAAACX0/dz2VCU3GPzQ/s320/resteasy-cliente-java.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
En el cliente javascript si usamos el formato json nos será más cómodo tratarlo en el navegador:

&lt;script src="https://gist.github.com/picodotdev/5014791.js?file=helloworld-resteasy.jsp"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5014791/raw/helloworld-resteasy.jsp"&gt;helloworld-resteasy.jsp&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

En las siguientes imágenes las peticiones que se realizan desde el navegador con el cliente javascript, en la columna type se puede ver el tipo de datos devuelto en las peticiones:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-BaEJsmw9qTs/UTEEHQipYvI/AAAAAAAACaw/hTnw7rHWMag/s1600/resteasy-peticiones.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="86" src="http://2.bp.blogspot.com/-BaEJsmw9qTs/UTEEHQipYvI/AAAAAAAACaw/hTnw7rHWMag/s200/resteasy-peticiones.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-R7tqp6Qhxj4/UTEEHahvVAI/AAAAAAAACas/_uBRpi4w9sw/s1600/resteasy-json.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="70" src="http://4.bp.blogspot.com/-R7tqp6Qhxj4/UTEEHahvVAI/AAAAAAAACas/_uBRpi4w9sw/s200/resteasy-json.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Para que el ejemplo funcione deberemos incluir las dependencias org.jboss.resteasy:resteasy-jaxb-provider:2.3.5.Final y org.jboss.resteasy:resteasy-jettison-provider:2.3.5.Final.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/5014791.js?file=build.gradle"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/5014791/raw/build.gradle"&gt;build.gradle&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Para finalizar, comentar que la combinación de ofrecer en la aplicación una API REST junto con librerías como &lt;a href="http://backbonejs.org/"&gt;Backbone.js&lt;/a&gt; que proporcionan un Modelo-Vista-Controlador (MVC) en el navegador accediendo al servidor para consumir el servicio puede dar lugar a aplicaciones más flexibles y fáciles de desarrollar entre otras cosas. Y esa es la tendencia hacia la que están evolucionado las aplicaciones web a medida que el propio lenguaje javascript y los navegadores aumentan sus capacidades.&lt;br /&gt;
&lt;br /&gt;
En el siguiente enlace puedes encontrar el &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HelloWorldRESTEasy"&gt;código fuente completo&lt;/a&gt; de este ejemplo y probarlo tu mismo en un servidor de aplicaciones como Tomcat. La siguiente entrada será como &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/03/integracion-tapestry-con-resteasy.htm.html"&gt;integrar RESTEasy con un framework de desarrollo web como Apache Tapestry&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://www.jboss.org/resteasy"&gt;http://www.jboss.org/resteasy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://backbonejs.org/"&gt;http://backbonejs.org&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HelloWorldRESTEasy"&gt;Código fuente completo del ejemplo Hola Mundo con RESTEasy&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/ZxQF-Vys2yA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1018335039820031572/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/02/devolver-xml-json-o-html-con-resteasy.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1018335039820031572?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1018335039820031572?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/ZxQF-Vys2yA/devolver-xml-json-o-html-con-resteasy.html" title="Devolver xml, json o html con RESTEasy" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s72-c/resteasy.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/02/devolver-xml-json-o-html-con-resteasy.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4HR3s_cCp7ImA9WhBQF0Q.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-3776470218435274697</id><published>2013-02-16T02:57:00.000+01:00</published><updated>2013-03-20T15:48:56.548+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-20T15:48:56.548+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Cliente javascript y java de servicio web REST con RESTEasy</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="RESTEasy" border="0" height="142" src="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s200/resteasy.png" title="RESTEasy" width="200" /&gt;&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;/div&gt;
Una vez que hemos desarrollado un &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html"&gt;servicio web REST&lt;/a&gt; tendremos que obtener un cliente para consumirlo ya sea con javascript, Java u otro lenguaje. En cualquier caso los clientes son mucho más sencillos de obtener en RESTeasy que en los servicios web basados en mensajes SOAP y XML haciendo uso de otras librerías a partir de la definición contenida en el WSDL como vamos a ver. Además, de poder ser consumidos desde un navegador a través de javascript.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;/div&gt;
Para el cliente Java deberemos tener la interfaz de ese web service con los métodos ofrecidos en el servicio, con las anotaciones adecuadas y que deberemos implementar en una clase. La interfaz del ejemplo &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html"&gt;Ejemplo sencillo de web service con RESTEasy&lt;/a&gt; sería:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4965023.js?file=HelloWorldResource.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4965023/raw/HelloWorldResource.java"&gt;HelloWorldResource.java&lt;/a&gt;&lt;br /&gt;&lt;/noscript&gt;

Y la implementación:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4965023.js?file=HelloWorldResourceImpl.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4965023/raw/HelloWorldResourceImpl.java"&gt;HelloWorldResourceImpl.java&lt;/a&gt;&lt;br /&gt;&lt;/noscript&gt;

Una vez tengamos la interfaz del web service REST y su implementación podemos hacer un programa java que lo use como cliente:&lt;br /&gt;
&lt;br /&gt;
Apartir de esa interfaz podemos obtener el cliente y un programa que lo invoca con el siguiente código. La parte importante está en el uso de las clases RegisterBuiltin y ProxyFactory:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4965023.js?file=HelloWorldResourceClient.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4965023/raw/HelloWorldResourceClient.java"&gt;HelloWorldResourceClient.java&lt;/a&gt;&lt;br /&gt;&lt;/noscript&gt;

Y eso es lo mínimo necesario para consumir los web services desde java ¿te pareció sencillo?, pues para consumirlos desde un navegador con javascript es aún más sencillo, veamos como. RESTEasy proporciona un servlet que genera un archivo js con los clientes javascript de nuestros servicios web REST, lo único que deberemos hacer es incluir ese servlet en el web.xml de nuestra aplicación web y usarlo en la página html en la que queramos hacer uso de él.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4965023.js?file=web.xml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4965023/raw/web.xml"&gt;web.xml&lt;/a&gt;&lt;br /&gt;&lt;/noscript&gt;

El servlet que generará el javscript, resteasy-jsapi, debe ser cargado después del servlet de RESTeasy por eso los valores de los load-on-startup en las etiquetas servlet, si no lo hiciéramos así obtendríamos la siguiente excepción de error:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4965023.js?file=StackTrace"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4965023/raw/StackTrace"&gt;StackTrace&lt;/a&gt;&lt;br /&gt;&lt;/noscript&gt;

En el archivo helloworld-resteasy.jsp está como es la llamada al web service desde el navegador, básicamente consiste en generar un html que incluya la etiqueta script haciendo referencia al javascript a obtener en el navegador, contendrá los clientes javascript de todos nuestros web services REST. Aunque en el uso de los clientes las peticiones que se producen a través de la red estas operaciones son totalmente transparentes para nosotros, en cada correspondiente cliente javascript tendremos disponibles unos métodos que se encargarán de realizar y de la forma adecuada las peticiones teniendo entre otras cosas en cuenta como enviar los parámetros de los métodos. Una cosa a tener en cuenta es que en la interfaz del web service REST no debemos usar sobrecarga, mismo nombre de método con diferentes parámetros, ya que de otra manera el cliente javascript no sabe distinguir a que método del Servicio web llamar.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4965023.js?file=helloworld-resteasy.jsp"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4965023/raw/helloworld-resteasy.jsp"&gt;helloworld-resteasy.jsp&lt;/a&gt;&lt;br /&gt;&lt;/noscript&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-qIi8Bw71Uw4/UR7rRheRrrI/AAAAAAAACWA/EGgEHueV-7A/s1600/resteasy-java.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="133" src="http://3.bp.blogspot.com/-qIi8Bw71Uw4/UR7rRheRrrI/AAAAAAAACWA/EGgEHueV-7A/s200/resteasy-java.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-k2_iUN5SMRA/UR7rRo3IK3I/AAAAAAAACWI/Cwa_g9pt4AM/s1600/resteasy-javascript.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="100" src="http://2.bp.blogspot.com/-k2_iUN5SMRA/UR7rRo3IK3I/AAAAAAAACWI/Cwa_g9pt4AM/s200/resteasy-javascript.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-3BK3jiBXOFU/UR7rRu95BPI/AAAAAAAACWM/t5vEcBDyUBE/s1600/resteasy-cliente-javascript.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="83" src="http://4.bp.blogspot.com/-3BK3jiBXOFU/UR7rRu95BPI/AAAAAAAACWM/t5vEcBDyUBE/s200/resteasy-cliente-javascript.png" width="200" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Recursos que pide el cliente Javascript&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
El último paso que tendremos que aprender es &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/devolver-xml-json-o-html-con-resteasy.html"&gt;como devolver otros tipos de datos más complejos como json, xml, html u otros&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://es.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html"&gt;Ejemplo sencillo de servicio web con RESTEasy&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HelloWorldRESTEasy"&gt;Código fuente completo del ejemplo Hola Mundo con RESTEasy&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/Fcr_R9dEeYI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/3776470218435274697/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/02/cliente-javascript-y-java-de-servicio-web-resteasy.html#comment-form" title="4 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3776470218435274697?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3776470218435274697?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/Fcr_R9dEeYI/cliente-javascript-y-java-de-servicio-web-resteasy.html" title="Cliente javascript y java de servicio web REST con RESTEasy" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s72-c/resteasy.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/02/cliente-javascript-y-java-de-servicio-web-resteasy.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMBQXYzfCp7ImA9WhBWF00.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-3618300324673965445</id><published>2013-02-09T00:54:00.005+01:00</published><updated>2013-04-11T20:04:10.884+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-11T20:04:10.884+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Ejemplo sencillo de servicio web con RESTEasy</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="RESTEasy" border="0" src="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s1600/resteasy.png" title="RESTEasy" /&gt;&lt;/div&gt;
Las &lt;a href="http://en.wikipedia.org/wiki/Service-oriented_architecture"&gt;arquitecturas orientadas a servicios&lt;/a&gt; (SOA) no son nuevas y existen desde hace ya bastante tiempo. Surgen por la necesidad de las aplicaciones de comunicarse entre ellas para intercambiar información y colaborar. Dada la heterogeneidad de las mismas aplicaciones en el lenguaje de programación o de representación de datos requieren un lenguaje común con el que intercambiar información, para ello se puede emplear un lenguaje de marcas como &lt;a href="http://es.wikipedia.org/wiki/Extensible_Markup_Language"&gt;XML&lt;/a&gt; y para el intercambio de mensajes &lt;a href="http://es.wikipedia.org/wiki/Simple_Object_Access_Protocol"&gt;SOAP&lt;/a&gt; (Simple Object Access Protocol) sobre el protocolo HTTP. Otras opciones como &lt;a href="http://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture"&gt;CORBA&lt;/a&gt;&amp;nbsp;los mensajes se transmitían de forma binaria. Pero los servicios web basados en XML y SOAP no están exentos de problemas, los mensajes SOAP son bastante complejos y para algunas necesidades no son fáciles de construir o cómodas de usar, por ejemplo desde un navegador o un dispositivo móvil empleando javascript.&lt;br /&gt;
&lt;br /&gt;
Dado el auge u omnipresencia que están tomando los servicios basados en internet, la web, los navegadores y javasctipt muchos de los sitios más importantes están evolucionando hacia servicios &lt;a href="http://en.wikipedia.org/wiki/Representational_state_transfer"&gt;REST&lt;/a&gt;&amp;nbsp;por su simplicidad de ser consumidos y fácil acceso, muchos sitios ofrecen una API basada en REST para que terceros puedan integrase con ellos, este es el principio para poder construir &lt;a href="http://es.wikipedia.org/wiki/Mashup_(aplicaci%C3%B3n_web_h%C3%ADbrida)"&gt;mashups&lt;/a&gt;. Algunas de sus características son que emplean el protocolo HTTP y sus diferentes métodos (GET, POST, DELETE y PUT, etc.) asignando a cada uno de ellos una operación de las denominadas CRUD (create -&amp;gt; PUT/POST, retrieve -&amp;gt; GET, update -&amp;gt; PUT/POST, delete -&amp;gt; DELETE). Pueden devolver datos en cualquier formato pero normalmente se emplea JSON o XML. En el siguiente enlace, muy recomendable, sobre &lt;a href="http://asiermarques.com/2013/conceptos-sobre-apis-rest/"&gt;algunos conceptos sobre las APIs REST&lt;/a&gt;&amp;nbsp;está muy bien explicado las ideas sobre las que se basan estos servicios.&lt;br /&gt;
&lt;br /&gt;
Para hacer sevicios web basados en REST con Java disponemos de varios frameworks entre ellos &lt;a href="http://www.restlet.org/"&gt;Restlet&lt;/a&gt; o &lt;a href="http://jersey.java.net/"&gt;Jersey&lt;/a&gt;, sin embargo, me ha convencido el ofrecido por la comunidad de JBoss, &lt;a href="http://www.jboss.org/resteasy"&gt;RESTEasy&lt;/a&gt;, tiene una muy buena &lt;a href="http://docs.jboss.org/resteasy/docs/"&gt;documentación&lt;/a&gt; que está bien explicada y con la que es muy fácil iniciarse. Por lo tanto a continuación voy a poner lo necesario para implementar el ejemplo Hola Mundo con RESTEasy en una aplicación web java que puede ser &lt;a href="http://elblogdepicodev.blogspot.com.es/2012/02/5-opciones-de-hosting-para-aplicaciones.html"&gt;hospedada en cualquier servidor de aplicaciones como Tomcat o JBoss&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Con RESTEasy los servicios web son &lt;a href="http://en.wikipedia.org/wiki/Plain_Old_Java_Object"&gt;POJO&lt;/a&gt; (Plain Old Java Object) a los que se les añade la fucionalidad mendiante anotaciones. Veamos como de simple podría ser el típico ejemplo sencillo de web service Hola mundo como demostración.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4742921.js?file=HelloWorldResource.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4742921/raw/HelloWorldResource.java"&gt;HelloWorldResource.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Aquí vemos varias anotaciones @Path, @GET y @PathParam. Con la primera estamos indicando parte de la URL en la que el web service responderá, con la segunda indicamos que el método HTTP que llame a esa URL deberá ser get y con la tercera recogemos un parámetro indicado en la URL. La implementación sería:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4742921.js?file=HelloWorldResourceImpl.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4742921/raw/HelloWorldResourceImpl.java"&gt;HelloWorldResourceImpl.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Una vez que disponemos del web service deberemos añadir al archivo web.xml las cosas necesarias para que RESTEasy recoja las llamadas a las URLs de los servicios web y los invoque, entre ellas el servlet de nombre resteasy de la clase HttpServletDispatcher. El web.xml sería el siguiente:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4742921.js?file=web.xml"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4742921/raw/web.xml"&gt;web.xml&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Lo básico a añadir es el servlet de RESTEasy y el correspondiente servlet-mapping, también deberemos indicar el parámetro de contexto «resteasy.servlet.mapping.prefix» con el prefijo de la ruta del servlet-mapping del servlet de RESTEasy. Con esta configuración todas las URLs que lleguen a la aplicación web y con la ruta «/rest/» corresponderán a un web service REST, una cosa a destacar es que los servicios web pueden convivir con con el resto de la aplicación independientemente del framework que uticemos para desarrollarla, lo único que deberemos hacer es indicar un prefijo para el servlet de RESTEasy tal y como hemos hecho en el ejemplo.&lt;br /&gt;
&lt;br /&gt;
Pero... ¿Como sabe RESTEasy cuales son los servicios web de la aplicación? La forma de indicarle a RESTEasy los servicios web es mediante una clase que extiende de &lt;a href="http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/Application.html"&gt;javax.ws.rs.core.Application&lt;/a&gt; y utilizando el parámetro de inicialización javax.ws.rs.Application para el servlet de RESTEasy que se puede ver en el web.xml. La implementación no tiene más que un Set con las implementaciones de los servicios web, en el caso del ejemplo:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/picodotdev/4742921.js?file=Application.java"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/picodotdev/4742921/raw/Application.java"&gt;Application.java&lt;/a&gt;&lt;br/&gt;&lt;/noscript&gt;

Para finalizar, comentar que RESTEasy se puede configurar también como un filtro, lo que tiene algunas ventajas como descubrir de forma dinámica los servicios web sin necesidad de la clase Application. El resultado del servicio web invocado desde un navegador web es:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-CrtGpMzFS2c/URWRiKGVxaI/AAAAAAAACUg/NUtNOrz_l6M/s1600/resteasy-navegador.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="58" src="http://4.bp.blogspot.com/-CrtGpMzFS2c/URWRiKGVxaI/AAAAAAAACUg/NUtNOrz_l6M/s320/resteasy-navegador.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;br /&gt;&lt;/div&gt;
Este es el primer artículo de unos que escribiré sobre RESTEasy, en el siguiente explicaré&amp;nbsp;&lt;a href="http://elblogdepicodev.blogspot.com/2013/02/cliente-javascript-y-java-de-servicio-web-resteasy.html"&gt;como crear un cliente javascript o java para consumir este servicio web&lt;/a&gt; y en el último hablaré de &lt;a href="http://elblogdepicodev.blogspot.com/2013/02/devolver-xml-json-o-html-con-resteasy.html"&gt;como devolver datos en formato JSON o XML desde el web service&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/HelloWorldRESTEasy"&gt;Código fuente Hola Mundo con RESTEasy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://asiermarques.com/2013/conceptos-sobre-apis-rest/"&gt;Conceptos sobre APIs REST&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.jboss.org/resteasy"&gt;http://www.jboss.org/resteasy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://stackoverflow.com/questions/4131968/resteasy-path-requiring-a-full-path"&gt;http://stackoverflow.com/questions/4131968/resteasy-path-requiring-a-full-path&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/ABAd9VaS8x0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/3618300324673965445/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html#comment-form" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3618300324673965445?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3618300324673965445?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/ABAd9VaS8x0/ejemplo-sencillo-de-web-service-con-resteasy.html" title="Ejemplo sencillo de servicio web con RESTEasy" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-KSg4_P3c29c/URWSVgCfoNI/AAAAAAAACUs/4-wR1eFK_JY/s72-c/resteasy.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/02/ejemplo-sencillo-de-web-service-con-resteasy.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcFRXY4eyp7ImA9WhBTF0s.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1693213389897988140</id><published>2013-02-01T19:28:00.000+01:00</published><updated>2013-02-13T16:06:54.833+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-13T16:06:54.833+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="raspberrypi" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Reproductor de radios Jamendo con Python</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Jamendo" border="0" height="75" src="http://3.bp.blogspot.com/-MkfiikeXCJU/UQwBjMDcBdI/AAAAAAAACRo/Kmik51OGxUI/s200/jamendo.png" title="Jamendo" width="200" /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Python" border="0" src="http://1.bp.blogspot.com/-vocIJ5QRxuY/UQwBjEIhvEI/AAAAAAAACRs/W46jlcfomvM/s1600/python.png" title="Python" /&gt;&lt;/div&gt;
Si sigues este blog desde hace un tiempo sabrás que de vez en cuando comento alguna cosa relacionada aunque sea de forma indirecta con la Raspberry Pi ya que es el computador que uso para realizar las descargas via torrent y para reproducir música principalmente con cmus. Dado que la uso sin ningún entorno de interfaz gráfica sino simplemente conectándome via SSH desde mi portátil y trabajando a través de comandos desde la terminal casi todo lo que quiera hacer con ella lo tengo que hacer a través de la terminal.&lt;br /&gt;
&lt;br /&gt;
Para escuchar música suelo hacerlo de &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/01/4-formas-de-escuchar-musica-internet.html"&gt;varias maneras&lt;/a&gt;: escuchar mi propia música en mp3 con cmus, escuchar radios que emitan por internet vía streaming o también las radios de Jamendo a través del reproductor de su página web. &lt;a href="http://developer.jamendo.com/es/"&gt;Jamendo ofrece una API&lt;/a&gt; a través de la cual se pueden escuchar las radios. Esto es lo necesario para poder escuchar las radios de Jamendo desde la RPi utilizando la terminal. Como quería aprender algo de Pyhton y es uno de los objetivos RPi me puse manos a la obra a hacer un pequeño programa, aún no sabiendo nada de este lenguaje de programación, que me permitiese escuhar cualquiera de las radios de Jamendo via streaming.&lt;br /&gt;
&lt;br /&gt;
El resultado lo podéis encontrar en mi &lt;a href="https://github.com/picodotdev/elblogdepicodev/tree/master/JamendoRadioPlayer"&gt;repositorio de GitHub&lt;/a&gt; en un archivo que no llega a las 100 líneas de código Python. Básicamente utiliza la API Rest que ofrece Jamendo para los desarrolladores que devuelve la información en formato JSON. Permite listar las radios disponibles en Jamendo según el estilo de música y los identificativos que utilizaremos luego para indicar al programa la radio que queremos escuchar.&lt;br /&gt;
&lt;br /&gt;
Para usar el programa necesitaremos tener &lt;a href="https://www.archlinux.org/packages/extra/x86_64/python/"&gt;Python&lt;/a&gt; instalado y dado el que reproduce la música realmente es el programa &lt;a href="https://www.archlinux.org/packages/extra/x86_64/mpg123/"&gt;mpg123&lt;/a&gt; necesitaremos instalar también su paquete.&lt;br /&gt;
&lt;br /&gt;
Después de habernos descargado el programa y habiéndole dado permisos de ejecución con:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4693063.js?file=script-1.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/raw/4693063/script-1.sh"&gt;Código de este Gist de GitHub&lt;/a&gt;&lt;/noscript&gt;

&lt;br/&gt;
Podemos obtener un listado de las radios con su nombre e identificativo usando:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4693063.js?file=script-2.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/raw/4693063/script-2.sh"&gt;Código de este Gist de GitHub&lt;/a&gt;&lt;/noscript&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-qAq_vhliJOc/UQwIqmtP9AI/AAAAAAAACTE/VyLRv5wfKNQ/s1600/JamendoRadioPlayer-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="125" src="http://1.bp.blogspot.com/-qAq_vhliJOc/UQwIqmtP9AI/AAAAAAAACTE/VyLRv5wfKNQ/s320/JamendoRadioPlayer-1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Una vez que sepamos la radio que queremos escuhar lo haríamos con, en caso de que fuese la radio Classical:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4693063.js?file=script-3.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="https://gist.github.com/raw/4693063/script-3.sh"&gt;Código de este Gist de GitHub&lt;/a&gt;&lt;/noscript&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-l9bmYBSoOZ0/UQwIyFvyJHI/AAAAAAAACTM/JOAVHeQS1aY/s1600/JamendoRadioPlayer-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="125" src="http://1.bp.blogspot.com/-l9bmYBSoOZ0/UQwIyFvyJHI/AAAAAAAACTM/JOAVHeQS1aY/s320/JamendoRadioPlayer-2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Este programa se puede usar en cualquier sitio que tenga esas dependencias aunque en un ordenador de escritorio o portátil no nos sea imprescindible como en el caso de la Raspberry Pi ya que en los primeros podremos usar el reproductor web de Jamendo. Aún así aquí está para quien le pueda resultar útil como a mi. El programa no capturará todas las condiciones de error que se puedan producir, tener en cuenta que está realizado por alguien que sabe programar, conoce el lenguaje y API de Java pero no de Python. En cualquier caso si alguien cree que puede ser mejorado solo tiene que comentarmelo, enviarme un parche, ... ;)&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/06/guia-instalacion-raspberry-pi-con-arch.html"&gt;Guías de instalación Raspberry Pi&lt;/a&gt; y &lt;a href="http://elblogdepicodev.blogspot.com.es/2012/05/raspberry-pi-desempaquetado-unboxing.html"&gt;desempaquetado&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/01/4-formas-de-escuchar-musica-internet.html"&gt;4 formas de escuchar música a través de internet&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/OL_npW9I-TM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1693213389897988140/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/02/reproductor-de-radios-jamendo-con-python.html#comment-form" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1693213389897988140?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1693213389897988140?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/OL_npW9I-TM/reproductor-de-radios-jamendo-con-python.html" title="Reproductor de radios Jamendo con Python" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-MkfiikeXCJU/UQwBjMDcBdI/AAAAAAAACRo/Kmik51OGxUI/s72-c/jamendo.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/02/reproductor-de-radios-jamendo-con-python.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMNQX87fyp7ImA9WhNaF0k.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1554639912203231471</id><published>2013-01-25T18:19:00.000+01:00</published><updated>2013-02-01T19:31:30.107+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-01T19:31:30.107+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="VLC" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="raspberrypi" /><title>4 formas de escuchar música a través de internet</title><content type="html">Si nos gusta la música probablemente tendremos una colección amplia de canciones de diferentes artistas y géneros, sin embargo, por muy amplia que sea tal vez en algún momento nos apetezca escuchar algo diferente a la música que tenemos o nos apetezca escuchar la música del momento a través de alguna radio que emita por internet via streaming.&lt;br /&gt;
&lt;br /&gt;
Muchas radios ya emiten a través de internet y por tanto las podemos escuchar desde el ordenador siempre y cuando tengamos una conexión a internet. Si no queremos usar el applet flash que normalmente proporciona cada radio en su propia página web podemos usar VLC, Banshee o incluso desde la terminal o consola. Para esto último ello basta con que tengamos instalado el programa &lt;a href="https://www.archlinux.org/packages/extra/x86_64/mpg123/"&gt;mpg123&lt;/a&gt; y conozcamos la URL en la que emite la radio lo cual a veces no es sencillo ni viendo el código fuente de la página del reproductor del applet flash pero escuchar música o radios por internet disponemos de varias opciones más, estas son algunas de ellas:&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
VLC&lt;/h4&gt;
El &lt;a href="http://www.videolan.org/vlc/"&gt;reproductor multimedia VLC&lt;/a&gt; además de reproducir vídeos, música en prácticamente todo formato conocido de medio puede reproducir música por internet de forma bastante sencilla para el usuario. Solo hay que ir a la opción Ver &amp;gt; Lista de reproducción en la opción Internet de la lista de reproducción disponemos de varias opciones en cuanto a música y radios:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Free Music Charts: aunque parece que esta opción ha dejado de actualizarse desde hace bastante tiempo.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dir.xiph.org/index.php"&gt;Icecast Radio Directory&lt;/a&gt;: esta es una lista de radios que emiten por internet, hay un montón de radios que podemos escuchar solo deberemos encontrar una que nos guste. Algunas de las radios emiten la música que está pegando fuerte en el momento.&lt;/li&gt;
&lt;li&gt;Jamendo Selections: &lt;a href="http://www.jamendo.com/es/"&gt;Jamendo&lt;/a&gt; es una colección de música en la que la mayor parte de la misma esta con licencia creative-commons que nos permite además de escucharla también con el propio reproductor de Jamendo en su propia página web poder descarganos las canciones a nuestro ordenador en formato mp3. Con VLC podemos escuchar la música emitida en las radios de Jamendo, hay radios para varios estilos de música: Rock, Pop, Jazz, Dance, Hiphop+Rap, World+Reggae, Lounge+Ambient. También tenemos una lista de las 100 pistas más populares, además de cada estilo junto con las 100 y 20 pistas más populares de la semana.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Con VLC también podemos &lt;a href="http://elblogdepicodev.blogspot.com.es/2010/02/escuchar-radios-traves-de-internet-con.html"&gt;Escuchar radios a través de internet&lt;/a&gt;&amp;nbsp;como la Radio Nacional de España.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-HRNn-M_xYuM/UQK6j6qmddI/AAAAAAAACPc/SUgROCihlhE/s1600/vlc-icecast.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="166" src="http://1.bp.blogspot.com/-HRNn-M_xYuM/UQK6j6qmddI/AAAAAAAACPc/SUgROCihlhE/s200/vlc-icecast.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-Dve8jg0aGKY/UQK6jwXD4NI/AAAAAAAACPg/F4GsTSdFBRM/s1600/vlc-jamendo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="166" src="http://2.bp.blogspot.com/-Dve8jg0aGKY/UQK6jwXD4NI/AAAAAAAACPg/F4GsTSdFBRM/s200/vlc-jamendo.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Linea de comandos con mpg123&lt;/h4&gt;
El programa &lt;a href="http://www.mpg123.de/"&gt;mpg123&lt;/a&gt; nos permite escuchar música desde la linea de comandos con un consumo ridículo de recursos y sin necesidad de un programa con una interfaz gráfica, lo que nos puede ser útil para la &lt;a href="http://www.raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt;. Tan sólo deberemos conocer la URL en la que emite la radio. La mayoría de las radios del &lt;a href="http://dir.xiph.org/index.php"&gt;Icecast Radio Directory&lt;/a&gt; proporcionan un enlace M3U con la dirección de la radio y algunas radios de cada país como el caso de la radio nacional de España (RNE):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://radio1.rtve.stream.flumotion.com/rtve/radio1.mp3.m3u"&gt;RNE 1&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://radioclasica.rtve.stream.flumotion.com/rtve/radioclasica.mp3.m3u"&gt;Radio Clásica&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://radio3.rtve.stream.flumotion.com/rtve/radio3.mp3.m3u"&gt;Radio 3&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://radio4.rtve.stream.flumotion.com/rtve/radio4.mp3.m3u"&gt;Radio 4&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://radio5.rtve.stream.flumotion.com/rtve/radio5.mp3.m3u"&gt;Radio 5 Todo Noticias&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://radioexterior.rtve.stream.flumotion.com/rtve/radioexterior.mp3.m3u"&gt;Radio Exterior&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.listenlive.eu/spain.html"&gt;Lista de radios nacionales&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Teniendo la dirección URL de la radio la reproducimos con:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4636223.js?file=mpg123.sh"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;div class="codigo"&gt;
&lt;pre&gt;&lt;a href="https://gist.github.com/raw/4636223/mpg123.sh"&gt;[Código de este Gist]&lt;/a&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/noscript&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-zdXHL8t2vnU/UQK6_CZovVI/AAAAAAAACPw/J4hEih4RE64/s1600/mpg123.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="110" src="http://2.bp.blogspot.com/-zdXHL8t2vnU/UQK6_CZovVI/AAAAAAAACPw/J4hEih4RE64/s200/mpg123.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-fIohQ_GVWUg/UQK6_MKIkcI/AAAAAAAACPs/Q7uUni_R9AM/s1600/mpg123-radio.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="64" src="http://2.bp.blogspot.com/-fIohQ_GVWUg/UQK6_MKIkcI/AAAAAAAACPs/Q7uUni_R9AM/s200/mpg123-radio.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
cmus&lt;/h4&gt;
&lt;a href="http://cmus.sourceforge.net/"&gt;cmus&lt;/a&gt; es un reproductor de música basado en la consola que además de permitirnos escuchar nuestra música local nos permite también escuchar las radios que emitan via streaming. Al igual que con mpg123 solo necesitaremos la URL en la que emite la radio. Una vez que tenemos la URL y cmus iniciado deberemos teclear lo siguiente para añadir la radio a nuestra colección de música en cmus:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4636223.js?file=cmus-comando"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;div class="codigo"&gt;
&lt;pre&gt;&lt;a href="https://gist.github.com/raw/4636223/cmus-comando"&gt;[Código de este Gist]&lt;/a&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/noscript&gt;

&lt;br /&gt;
La ventaja de cmus sobre mpg123 es que no hará falta que recordemos o obtengamos la URL cada vez que queremos reproducir una emisora ya que con los comandos anteriores, dentro de cmus quedarán añadidos a nuestra librería en el artista «Stream».&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-TYlflfG7w8U/UQK7LzN8AfI/AAAAAAAACP8/ziHGx5BZKVM/s1600/cmus.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="146" src="http://1.bp.blogspot.com/-TYlflfG7w8U/UQK7LzN8AfI/AAAAAAAACP8/ziHGx5BZKVM/s320/cmus.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Jamendo&lt;/h4&gt;
&lt;a href="http://www.jamendo.com/es/"&gt;Jamendo&lt;/a&gt; es una colección de música realizada por artistas no tan comerciales como la que solemos escuchar normalmente en las radios y que muchos artistas publican sus creaciones con una licencia &lt;a href="http://creativecommons.org/"&gt;creative-commons&lt;/a&gt; que nos permite incluso descargarla para un uso privado por lo que si estamos cansados de escuchar siempre lo mismo puede ser un viento de aire fresco para nuestros oídos además de poder descubrir nuevos grupos. La música la podemos escuchar desde la propia página de Jamendo con el reproductor integrado en su web con el navegador. Podemos escuchar cualquiera de las &lt;a href="http://www.jamendo.com/es/radios"&gt;radios con diferentes estilos de música&lt;/a&gt; o utilizar el &lt;a href="http://www.jamendo.com/es/search"&gt;buscador&lt;/a&gt; para seleccionar algún estilo de música más específico.&lt;br /&gt;
&lt;br /&gt;
Si quieres escuchar las radios de Jamendo desde la terminal o desde la Raspberry Pi puedes hacerlo utilizando este &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/02/reproductor-de-radios-jamendo-con-python.html"&gt;reproductor programado en Python&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-PFIeySysZcM/UQK7YasnPhI/AAAAAAAACQE/RI__HhjpFDg/s1600/Jamendo-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-PFIeySysZcM/UQK7YasnPhI/AAAAAAAACQE/RI__HhjpFDg/s200/Jamendo-3.png" width="151" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-Jl4wDGyyzTU/UQK7YfJ1TXI/AAAAAAAACQI/8do2DZq7Lck/s1600/Jamendo-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="186" src="http://2.bp.blogspot.com/-Jl4wDGyyzTU/UQK7YfJ1TXI/AAAAAAAACQI/8do2DZq7Lck/s200/Jamendo-2.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-jJQscO_OsxE/UQK7YTWuh1I/AAAAAAAACQQ/Mcmtt5ceL6U/s1600/Jamendo-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-jJQscO_OsxE/UQK7YTWuh1I/AAAAAAAACQQ/Mcmtt5ceL6U/s200/Jamendo-1.png" width="190" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
En definitiva 4 formas diferentes de poder variar lo que solemos escuchar. &lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2010/02/escuchar-radios-traves-de-internet-con.html"&gt;Escuchar radios a través de internet con VLC&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/Pf-hbcTPY60" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1554639912203231471/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/01/4-formas-de-escuchar-musica-internet.html#comment-form" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1554639912203231471?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1554639912203231471?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/Pf-hbcTPY60/4-formas-de-escuchar-musica-internet.html" title="4 formas de escuchar música a través de internet" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-HRNn-M_xYuM/UQK6j6qmddI/AAAAAAAACPc/SUgROCihlhE/s72-c/vlc-icecast.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/01/4-formas-de-escuchar-musica-internet.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUARX4-cSp7ImA9WhNUGU0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-8319763412655892904</id><published>2013-01-11T13:44:00.001+01:00</published><updated>2013-01-11T13:44:04.059+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-11T13:44:04.059+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="bash" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="raspberrypi" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Autenticación SSH con clave privada y pública</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="OpenSSH" border="0" height="104" src="http://2.bp.blogspot.com/-xA1fbdip4ik/UOcpr317aSI/AAAAAAAACI8/ERcKs9Fm7IA/s320/openssh.gif" title="OpenSSH" width="320" /&gt;&lt;/div&gt;
Siguiendo el artículo de la semana pasada sobre &lt;a href="http://elblogdepicodev.blogspot.com.es/2013/01/dejar-procesos-vivos-de-conexion-ssh.html"&gt;como hacer que los programas no se terminasen al finalizar o perderse la conexión SSH&lt;/a&gt;&amp;nbsp;en esta entrada explicaré como conectarnos a un una máquina de forma remota mediante SSH con una clave pública y privada en vez de con la contraseña del usuario.&lt;br /&gt;
&lt;br /&gt;
Normalmente mediante contraseña nos conectamos a la máquina remota mediante el siguiente comando:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4510310.js?file=ssh-1.sh"&gt;&lt;/script&gt;

Esto nos pedirá la contraseña del usuario con el que trabajaremos en la máquina remota, en este caso la contraseña del usuario raspberrypi, y la deberemos introducir cada vez que realicemos la conexión. Si nos conectamos varias veces, realizamos conexiones a diferentes máquinas o nos conectamos con diferentes usuarios más pronto que tarde nos cansaremos de introducir la contraseña constantemente además de tener que estar recordando cada una de ellas si trabajamos con diferentes usuarios y asignamos a cada uno de ellos una diferente.&lt;br /&gt;
&lt;br /&gt;
Todo esto se puede solucionar usando una clave privada y otra pública SSH con las que no necesitaremos proporcionar ninguna contraseña ya que será la clave privada la que nos autenticará. No necesitaremos proporcionar ninguna contraseña siempre y cuando no protejamos la clave privada mendiante una contraseña para poder usarla.&lt;br /&gt;
&lt;br /&gt;
El par de claves SSH se generan con, se pueden utilizar otros algoritmos como RSA y DSA aunque ECDSA es el recomendado por OpenSSH por ofrecer la misma seguridad con un menor tamaño en bits para las claves. Sin embargo, en el ejemplo usaré RSA por el &lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=641082"&gt;siguiente bug de GNOME&lt;/a&gt;:
&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4510310.js?file=ssh-keygen-2.sh"&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-O_tlDDaLSmc/UPAGZxoTteI/AAAAAAAACMU/c4Umve9bBw8/s1600/ssh-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="168" src="http://4.bp.blogspot.com/-O_tlDDaLSmc/UPAGZxoTteI/AAAAAAAACMU/c4Umve9bBw8/s320/ssh-1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Al final del proceso veremos la huella digital de la clave, si en algún otro momento deseasemos ver la huella alfanumérica o visual podríamos hacerlo con:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4510310.js?file=ssh-keygen-2.sh"&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-lTXTECXwR1s/UPAGauC6fDI/AAAAAAAACMg/5LHoZcejE3k/s1600/ssh-5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="181" src="http://3.bp.blogspot.com/-lTXTECXwR1s/UPAGauC6fDI/AAAAAAAACMg/5LHoZcejE3k/s320/ssh-5.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Una vez que disponemos del par de claves deberemos subir la clave pública a la máquina que posteriormente nos conectaremos identificandonos con la clave privada. Lo hacemos con:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4510310.js?file=ssh-copy-id.sh"&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-Nv3J2vvSSH4/UPAGaKgK9WI/AAAAAAAACMc/R39PjwcP3zE/s1600/ssh-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="168" src="http://2.bp.blogspot.com/-Nv3J2vvSSH4/UPAGaKgK9WI/AAAAAAAACMc/R39PjwcP3zE/s320/ssh-2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Esta clave pública quedará como autorizada para la máquina y usuario indicados y será añadida al archivo ~/.ssh/autorihed_keys en la máquina destino. Si posteriormente quisieramos desautorizar alguna clave solo deberemos borrar la linea de la clave de ese archivo. Posteriormente deberemos indicar en la máquina origen las identidades que queremos que proporcione el comando SSH cuando nos conectemos a alguna máquina con:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4510310.js?file=ssh-add.sh"&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-CcrteE7EJlg/UPAGZ2Cb0WI/AAAAAAAACMY/MnObYdaxukI/s1600/ssh-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="168" src="http://2.bp.blogspot.com/-CcrteE7EJlg/UPAGZ2Cb0WI/AAAAAAAACMY/MnObYdaxukI/s320/ssh-3.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Si no hemos protegido la clave privada con ninguna clave la identidad quedará añadida con el comando SSH posterior, si hemos protegido la clave privada deberemos introducir la contraseña para poder añadir la identidad.&lt;br /&gt;
&lt;br /&gt;
Finalmente, nos conectaremos a la máquina con el comando SSH y  accederemos a la máquina destino sin tener que proporcionar ninguna clave:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4510310.js?file=ssh-2.sh"&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-infrhmeD-ss/UPAGaxIFTaI/AAAAAAAACMo/SQjeewss1SA/s1600/ssh-4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="168" src="http://2.bp.blogspot.com/-infrhmeD-ss/UPAGaxIFTaI/AAAAAAAACMo/SQjeewss1SA/s320/ssh-4.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
Referencia:&lt;br /&gt;
&lt;a href="https://wiki.archlinux.org/index.php/Secure_Shell#Client"&gt;https://wiki.archlinux.org/index.php/Secure_Shell#Client&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://wiki.archlinux.org/index.php/SSH_Keys"&gt;https://wiki.archlinux.org/index.php/SSH_Keys&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/06/guia-instalacion-raspberry-pi-con-arch.html"&gt;Guía instalación Raspberry Pi con Arch Linux ARM (Parte I, instalación base)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/06/guia-instalacion-raspberry-pi-con-arch_22.html"&gt;Guía instalación Raspberry Pi con Arch Linux ARM (Parte II, programas)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/yoj-Zjkg8A8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/8319763412655892904/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/01/autenticacion-ssh-con-clave-privada-y.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8319763412655892904?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8319763412655892904?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/yoj-Zjkg8A8/autenticacion-ssh-con-clave-privada-y.html" title="Autenticación SSH con clave privada y pública" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-xA1fbdip4ik/UOcpr317aSI/AAAAAAAACI8/ERcKs9Fm7IA/s72-c/openssh.gif" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/01/autenticacion-ssh-con-clave-privada-y.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMCRX0yeyp7ImA9WhNUGU0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-3614213764434755795</id><published>2013-01-04T20:22:00.000+01:00</published><updated>2013-01-11T13:47:44.393+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-11T13:47:44.393+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="bash" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="software libre" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="raspberrypi" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Dejar procesos vivos de conexión SSH con screen</title><content type="html">&lt;div imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="OpenSSH" border="0" height="105" src="http://2.bp.blogspot.com/-xA1fbdip4ik/UOcpr317aSI/AAAAAAAACI8/ERcKs9Fm7IA/s320/openssh.gif" title="OpenSSH" width="320" /&gt;&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="GNU/Linux" border="0" height="200" src="http://4.bp.blogspot.com/-gf0CBdHQ0EI/TQJrCzG9OZI/AAAAAAAAAiY/NcJAFNmleAw/s200/tux.png" title="GNU/Linux" width="169" /&gt;&lt;/div&gt;
El software libre y Linux es sorprendente, a veces nos surge una necesidad que no sabemos como resolver pero en el mundo del software es muy probable que el mismo problema se lo haya encontrado otra persona antes así que lo primero es buscar en internet, foros y ver cuales son las respuestas que obtiene esa persona que pregunta con el mismo o un problema parecido al nuestro para ver si alguien ha dado con la solución. Dependiendo del problema quizá descubramos un programa específico para resolver el problema.&lt;br /&gt;
&lt;br /&gt;
Este es el caso que me ha surgido recientemente con la &lt;a href="http://www.raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt;. Lo explico, a la RPi me conecto por SSH y dado que si se cierra esa conexión SSH se terminan todos los procesos lanzados en esa conexión SSH tengo que tener encendido el ordenador y la conexión SSH abierta hasta que termine de hacer lo que estaba haciendo. En la mayoría de los casos esto no es un problema pero para dejar la RPi reproduciendo música con &lt;a href="http://cmus.sourceforge.net/"&gt;cmus&lt;/a&gt; sí lo es ya que si se cierra la conexión SSH se termina el reproductor y se deja de escuchar la música, con lo que no puedo apagar el ordenador y dejar únicamente la RPi encendida que para reproducir música es mucho mejor que un ordenador porque no hay ruido de ventiladores y discos girando y la música se escucha más limpia sin ruidos adicionales por muy bajos que sean.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.gnu.org/software/screen/"&gt;Screen&lt;/a&gt;, es una herramienta que nos resolverá ese problema además de proporcionarnos alguna funcionalidad adicional interesante. Con esta herramienta podemos dejar una terminal viva y sus procesos ejecutándose aunque se cierre la conexión SSH. Su uso es conectarnos mediante SSH de forma normal, ejecutar:
&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4454876.js?file=screen.sh"&gt;&lt;/script&gt;
...que lanzará una segunda terminal en la sesión SSH y cuando queramos dejar la terminal de screen viva ejecutar CTRL+A y a continuación CTRL+D y ya podremos cerrar la terminal SSH original con exit, la terminal de screen y los procesos se seguirán ejecutando. Otra característica de screen es que podemos reconectarnos más tarde a la terminal de screen que dajamos viva, para ello nos volvemos a conectar mediante SSH y ejecutamos:
&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4454876.js?file=screen-r.sh"&gt;&lt;/script&gt;
por si este fuera poco el volvernos a reconectar a la terminal de screen más tarde lo podemos hacer desde cualquier ordenador, no tiene por que ser el mismo ordenador desde el que iniciamos la terminal de screen, con lo que podemos iniciar los procesos en un ordenador, desplazarnos de sitio y continuar en otro. Lo que en algunos otros casos puede ser muy útil.&lt;br /&gt;
&lt;br /&gt;
El paquete para instalarlo en Arch Linux es &lt;a href="https://www.archlinux.org/packages/extra/x86_64/screen/"&gt;screen&lt;/a&gt; y lo hacemos con:
&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/4454876.js?file=pacman.sh"&gt;&lt;/script&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://raspi.tv/tag/how-to-detach-a-process-on-ssh-without-killing-it"&gt;http://raspi.tv/tag/how-to-detach-a-process-on-ssh-without-killing-it&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://wiki.archlinux.org/index.php/GNU_Screen"&gt;https://wiki.archlinux.org/index.php/GNU_Screen&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2013/01/autenticacion-ssh-con-clave-privada-y.html"&gt;Autenticación SSH con clave privada y pública&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/06/guia-instalacion-raspberry-pi-con-arch.html"&gt;Guía instalación Raspberry Pi con Arch Linux ARM (Parte I, instalación base)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/06/guia-instalacion-raspberry-pi-con-arch_22.html"&gt;Guía instalación Raspberry Pi con Arch Linux ARM (Parte II, programas)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/KrS473x4T9k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/3614213764434755795/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/01/dejar-procesos-vivos-de-conexion-ssh.html#comment-form" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3614213764434755795?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3614213764434755795?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/KrS473x4T9k/dejar-procesos-vivos-de-conexion-ssh.html" title="Dejar procesos vivos de conexión SSH con screen" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-xA1fbdip4ik/UOcpr317aSI/AAAAAAAACI8/ERcKs9Fm7IA/s72-c/openssh.gif" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/01/dejar-procesos-vivos-de-conexion-ssh.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08CRXc_fSp7ImA9WhNUF04.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-9188698224994861478</id><published>2013-01-01T00:10:00.000+01:00</published><updated>2013-01-09T12:44:24.945+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-09T12:44:24.945+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="personalización" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Calendario Arch Linux 2013</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Arch Linux" border="0" height="66" src="http://3.bp.blogspot.com/-XVsmee9V9Vo/S_LjDl4iWZI/AAAAAAAAAQI/LcBAWOoav-s/s200/archlinux-official-dark.png" title="Arch Linux" width="200" /&gt;&lt;/div&gt;
El año pasado realice el calendario tematizado de Arch Linux y este en un poco de tiempo lo he &lt;a href="https://docs.google.com/open?id=0B4yquUFv3csral9EclZ5U29sMXc"&gt;modificado y adaptado al año 2013&lt;/a&gt;. Se trata de un modelo que después de varias dobleces y partes que hay que pegar se convierte en un dodecaedro que podemos tener encima de la mesa además para consultarlo alguna vez para decorar. Si eres usuario de otra distribución que no sea Arch Linux puedes descargarte el archivo svg contenido en el archivo zip del enlace anterior y modificarlo para adaptar los colores y el logotipo ya uses Fedora, OpenSUSE, Ubuntu, Mint, Debian, ...&lt;br /&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-F1UX2h_STHA/UOLZFRCplbI/AAAAAAAACHg/bP18tTle928/s1600/arch+linux+calendar+(2013).png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-F1UX2h_STHA/UOLZFRCplbI/AAAAAAAACHg/bP18tTle928/s320/arch+linux+calendar+(2013).png" width="247" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Calendario Arch Linux 2013&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div style="text-align: center;"&gt;
&lt;div style="text-align: left;"&gt;
El 2012 al final se me pasó y no lo construí ya que no dispongo de impresora, este 2013 espero poder imprimirlo en alguna fotocopistería y colocarlo junto a Tux y el &lt;a href="http://elblogdepicodev.blogspot.com.es/2010/03/arch-linux-cheat-cube.html"&gt;cheat cube de Arch Linux&lt;/a&gt;.&amp;nbsp;A continuación está proceso de ensamblado del calendario calendario y el resultado final.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-4Z0chjagyDY/UO1VVw_-oPI/AAAAAAAACKM/UpEOSkQR064/s1600/arch-linux-calendar-1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-4Z0chjagyDY/UO1VVw_-oPI/AAAAAAAACKM/UpEOSkQR064/s200/arch-linux-calendar-1.jpg" width="148" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-m-3vifu3m2U/UO1VVxq7_GI/AAAAAAAACKQ/JcMevheZ4Uk/s1600/arch-linux-calendar-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-m-3vifu3m2U/UO1VVxq7_GI/AAAAAAAACKQ/JcMevheZ4Uk/s200/arch-linux-calendar-2.jpg" width="148" /&gt;&lt;/a&gt;&lt;span style="text-align: left;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-OhFr5ZpZ2qY/UO1VWJWjLQI/AAAAAAAACKU/N3bqcSA9Qi8/s1600/arch-linux-calendar-3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-OhFr5ZpZ2qY/UO1VWJWjLQI/AAAAAAAACKU/N3bqcSA9Qi8/s200/arch-linux-calendar-3.jpg" width="148" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-lf92EzycVWQ/UO1VjBUVWVI/AAAAAAAACKs/pfu26yrR6Xw/s1600/arch-linux-calendar-4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-lf92EzycVWQ/UO1VjBUVWVI/AAAAAAAACKs/pfu26yrR6Xw/s200/arch-linux-calendar-4.jpg" width="148" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Además de este calendario también hace un poco de tiempo también adapté un cheat cube de Ubuntu a la distribución Arch Linux&amp;nbsp;por si alguien le coge gusto a esto del papel,&amp;nbsp;probablemente para un usuario avanzado no sea especialmente útil más bien tiene una funcionalidad decorativa.&lt;/div&gt;
&lt;/div&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Hu3KgunW-9Y/UO1WMfEhEUI/AAAAAAAACK8/S4LG853FhbQ/s1600/arch-linux-calendar-5.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="161" src="http://3.bp.blogspot.com/-Hu3KgunW-9Y/UO1WMfEhEUI/AAAAAAAACK8/S4LG853FhbQ/s320/arch-linux-calendar-5.jpg" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;El cheat cube, el calendario y tux&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
¡Que empecéis bien el 2013!&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="https://docs.google.com/open?id=0B4yquUFv3csral9EclZ5U29sMXc"&gt;Calendario Arch Linux 2013&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/oNWgUje3Mb4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/9188698224994861478/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2013/01/calendario-arch-linux-2013.html#comment-form" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/9188698224994861478?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/9188698224994861478?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/oNWgUje3Mb4/calendario-arch-linux-2013.html" title="Calendario Arch Linux 2013" /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-XVsmee9V9Vo/S_LjDl4iWZI/AAAAAAAAAQI/LcBAWOoav-s/s72-c/archlinux-official-dark.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2013/01/calendario-arch-linux-2013.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak4AQ3o5fCp7ImA9WhNVE0k.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-6966619328573439923</id><published>2012-12-24T12:32:00.000+01:00</published><updated>2012-12-24T12:35:42.424+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-12-24T12:35:42.424+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="hemeroteca" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>El blog de pico.dev te desea un 2013...</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img border="0" height="45" src="http://1.bp.blogspot.com/-cK1PH5pzXNc/TdKfToeduKI/AAAAAAAAApw/axtoypLaDfU/s320/El+blog+de+picodev+%2528watermark%2529.png" width="320" /&gt;&lt;/div&gt;
Otro año más que pasa para &lt;a href="http://elblogdepicodev.blogspot.com.es/"&gt;El blog de pico.dev&lt;/a&gt; y ya son casi tres años desde que empecé a escribir en él. Este año han sido 50 entradas las que he escrito un poco menos de una por semana, meta que me fijé como la adecuada para mi, que he ido haciendo con constancia y regularidad. Me gustaría poder dedicarle más tiempo al blog ya que ideas para entradas no me faltan a medida que voy curioseando sobre como se hacen o funcionan algunas cosas, ya tengo una buena cantidad&amp;nbsp;de ellas&amp;nbsp;acumuladas que durante el 2013 iré desarrollando. Aunque pueda parecer que no cada entrada requiere bastante tiempo de dedicación para aprender, investigar, programar algún ejemplo y luego escribir la entrada aunque luego se ve recompensado por los visitantes que las leéis, la satisfacción de escribir sobre lo que en cada momento más me interesa con total libertad y aunque pocos pero también con los comentarios de la gente que veo algunas cosas les han resultado útiles.&lt;br /&gt;
&lt;br /&gt;
Pasado otro año más toca hacer balance de los números de este blog. Si durante el 2011 fueron 34.271 visitas, 25.251 visitantes y 55.280 páginas vistas que ya me parecían muchas este 2012 a falta de unos días han sido 64.364 visitas de 52.075 visitantes con 86.432 páginas vistas, unos números que no son duplicar los del año anterior pero casi. Si a principios de año empezaba con entre 100 y 150 páginas vistas al día el año ha acabado con entre 250 y 400 páginas vistas diarias. También se nota en la publicidad AdSense que suele ser habitual ya tener unos ingresos de 3€ mensuales, a este ritmo el 2013 superaré los 70€ necesarios para que Google me recompense por la publicidad que ya veré de que manera se los devuelvo en parte a la comunidad ya sea pidiendo unas pegatinas con el logo de Arch Linux para el portátil si es así algunas puede que reparta por correo, una taza, o ...&lt;br /&gt;
&lt;br /&gt;
Estas son las entradas que he escrito durante la segunda mitad de este 2012 (en la &lt;a href="http://elblogdepicodev.blogspot.com.es/search/label/hemeroteca"&gt;etiqueta hemeroteca&lt;/a&gt; puedes ver las de otros semestres):&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Hemeroteca #2&lt;/h3&gt;
&lt;b&gt;Apache Tapestry&lt;/b&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/07/validaciones-de-datos-de-formularios.html"&gt;Validaciones de datos de formularios con Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/07/extender-las-validaciones-de-apache.html"&gt;Extender las validaciones de Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/11/como-devolver-un-archivo-con-apache.html"&gt;Como devolver un archivo con Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/11/como-enviar-un-archivo-con-apache.html"&gt;Como enviar un archivo con Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Programación&lt;/b&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/07/pruebas-unitarias-con-spock-y-mockito.html"&gt;Pruebas unitarias con Spock y Mockito&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/07/obtener-informacion-del-entorno-de-una.html"&gt;Obtener información del entorno de una aplicación web en Java&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/08/jodatime-y-como-integrarlo-con-hibernate.html"&gt;JodaTime y como integrarlo con Hibernate&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/08/analisis-estatico-de-codigo-con-pmd.html"&gt;Análisis estático de código con PMD&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/08/paginas-pagecatalog-servicestatus-e.html"&gt;Páginas PageCatalog, ServiceStatus e HibernateStatistics de Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/08/deshabilitar-el-parametro-jsessionid.html"&gt;Deshabilitar el parámetro jsessionid&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/10/crear-un-archivo-zip-con-java.html"&gt;Crear un archivo comprimido Zip con Java&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/11/como-crear-y-usar-sprites-en-paginas.html"&gt;Como crear y usar sprites en páginas HTML&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/12/color-en-la-terminal-con-jansi.html"&gt;Texto de la terminal en color con Jansi&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Una serie de artículos sobre BPM, Activiti y Drools&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/09/conceptos-sobre-procesos-de-negocio-bp.html"&gt;Conceptos sobre procesos de negocio (BP, BPM, BPMS, ...)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/09/procesos-de-negocio-con-activiti.html"&gt;Procesos de negocio con Activiti&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/10/usar-variables-en-un-proceso-de-negocio.html"&gt;Usar variables en un proceso de negocio con Activiti&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/10/tratamiento-de-errores-en-procesos-de.html"&gt;Tratamiento de errores en procesos de negocio con Activiti&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/10/reglas-de-negocio-con-drools-y-activiti.html"&gt;Reglas de negocio con Drools y Activiti&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Linux&lt;/b&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/09/systemd-reemplazo-de-sysvinit.html"&gt;systemd, reemplazo de sysvinit&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/09/guia-para-aprender-usar-vim.html"&gt;Guía para aprender a usar Vim&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/11/convertir-imagenes-entre-formatos-y.html"&gt;Convertir imágenes entre formatos y cambiar tamaño con ImageMagick&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Las entradas sobre Linux han sido muy pocoas y es que Arch Linux me da tan pocos problemas y no tengo que reinstalar cada pocos meses que la verdad no encuentro mucho en lo que escribir, aún así espero poder actualizar las guías de instalación de Arch Linux que ya se han quedado en parte obsoletas. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Análisis de productos&lt;/b&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/08/desempaquetado-unboxing-de-amazon.html"&gt;Desempaquetado («unboxing») de Amazon Kindle Touch&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/12/mi-guia-para-eleccion-de-portatil-nuevo.html"&gt;Mi guía para elección de portátil nuevo&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/12/desempaquetado-sony-vaio-e-14p.html"&gt;Desempaquetado Sony Vaio E 14P (SVE14)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com.es/2012/12/analisis-software-y-bios-sony-vaio-e-14p.html"&gt;Análisis software y BIOS de Sony Vaio E 14P&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Si no quieres perderte nada de lo que está por venir en este blog durante el año que viene no pierdas la oportunidad de suscribirte al feed con tu lector favorito.&lt;br /&gt;
&lt;br /&gt;
Ya no me queda más que ¡desearos un mejor y buen 2013!&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;img alt="¡elblogdepicodev te desea un próspero 2013!" border="0" src="http://2.bp.blogspot.com/-5VfRcweCOJY/UNWRxHfkO8I/AAAAAAAACE4/UmS2KaMsEcU/s400/christmas-tux-2012.png" title="¡elblogdepicodev te desea un próspero 2013!" width="550" /&gt;&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/wjsL4zA6wW4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/6966619328573439923/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2012/12/el-blog-de-picodev-te-desea-un-2013.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/6966619328573439923?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/6966619328573439923?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/wjsL4zA6wW4/el-blog-de-picodev-te-desea-un-2013.html" title="El blog de pico.dev te desea un 2013..." /><author><name>pico.dev</name><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><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-cK1PH5pzXNc/TdKfToeduKI/AAAAAAAAApw/axtoypLaDfU/s72-c/El+blog+de+picodev+%2528watermark%2529.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2012/12/el-blog-de-picodev-te-desea-un-2013.html</feedburner:origLink></entry></feed>
