<?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: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;DEEDRX0-fyp7ImA9WhRUF00.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900</id><updated>2012-01-28T00:24:34.357+01:00</updated><category term="juegos" /><category term="Minix" /><category term="GDM" /><category term="Microsoft" /><category term="hola" /><category term="personalización" /><category term="software libre" /><category term="java" /><category term="wallpaper" /><category term="software privativo" /><category term="GNU/Linux" /><category term="GRUB2" /><category term="bash" /><category term="Apple" /><category term="VLC" /><category term="gnome" /><category term="PHP" /><category term="otros" /><category term="programación" /><category term="blogger" /><category term="arch linux" /><category term="hemeroteca" /><category term="planeta código" /><category term="planeta arch linux" /><category term="tecnología" /><category term="ubuntu" /><category term="planeta linux" /><category term="google" /><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>91</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;AkIDSHo9eyp7ImA9WhRUFkQ.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-153281484742810690</id><published>2012-01-27T21:57:00.000+01:00</published><updated>2012-01-27T22:09:39.463+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-27T22:09:39.463+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="GNU/Linux" /><title>last, como obtener los últimos inicios de sesión</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Linux" border="0" height="200" src="http://4.bp.blogspot.com/-gf0CBdHQ0EI/TQJrCzG9OZI/AAAAAAAAAiY/NcJAFNmleAw/s200/tux.png" title="Linux" width="169" /&gt;&lt;/div&gt;
El comando last es una pequeña utilidad que nos permite conocer cuando se inició la sesión y cuando finalizó, con que usuario y desde que dirección IP se realizaron los últimos inicios de sesión a un sistema. Quizá esta información no sea de utilidad en alguna situación o para detectar intrusiones a un sistema. Su uso es muy simple:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;$ last -F -10&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Este comando nos permite conocer los ultimos 10 inicios de sesión en el sistema. Si queremos conocer los inicio de sesión realizados por un determinado usuario lo haríamos con:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;$ last -F -10 root&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Este comando tiene unos cuantos parámetros más que pueden consultarse en la página man del comando.&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/-AGLE_RMd6hE/TyMSbpB5nkI/AAAAAAAABC4/8m_xopWOrkY/s1600/last.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img alt="last" border="0" height="136" src="http://3.bp.blogspot.com/-AGLE_RMd6hE/TyMSbpB5nkI/AAAAAAAABC4/8m_xopWOrkY/s400/last.png" title="last" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-153281484742810690?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/2hInd3Lb-lU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/153281484742810690/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2012/01/last-como-obtener-los-ultimos-inicios.html#comment-form" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/153281484742810690?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/153281484742810690?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/2hInd3Lb-lU/last-como-obtener-los-ultimos-inicios.html" title="last, como obtener los últimos inicios de sesión" /><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/-gf0CBdHQ0EI/TQJrCzG9OZI/AAAAAAAAAiY/NcJAFNmleAw/s72-c/tux.png" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2012/01/last-como-obtener-los-ultimos-inicios.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMFSHY6eyp7ImA9WhRUE08.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-8003222930198638376</id><published>2012-01-21T17:02:00.001+01:00</published><updated>2012-01-23T15:20:19.813+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-23T15:20:19.813+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Errores de precisión, redondeo y representación con float y double</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;/div&gt;
Los tipos de datos float y double aunque muy usados y útiles tienen defectos, como que no son capaces de representar el resultado de ciertas operaciones ariméticas entre números reales con total precisión, en algunos casos solo son capaces de representar aproximaciones a ellos y que las operaciones las realizan en base dos. Al igual que nosotros no podemos repsentar 1/3 con números en base diez con total exactitud (solo una aproximación 0,333333333... con treses hasta el infinito) a las computadoras les pasa otro tanto de lo mismo, aunque estas guardan los números en base dos. Por tanto usarlos para hacer ciertas operaciones aritméticas, y siendo acumuladas varias de ellas, puede dar lugar a errores de redondeo y precisión en el resultado final. Por estos motivos usar estos tipos de datos no son los más adecuados para trabajar en una aplicación que calcule precios. Veámoslo con un ejemplo.&lt;br /&gt;
&lt;br /&gt;
Supongamos que tenemos una cantidad de dinero tal que 100,05 a la que aplicamos un 10% de descuento y posteriormente un 5% en concepto de impuestos. Las líneas de código que calculan esto son:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;import java.text.NumberFormat;

double amount = 100.05;
double discount = amount * 0.10;
double total = amount - discount;
double tax = total * 0.05;
double taxedTotal = tax + total;

NumberFormat money = NumberFormat.getCurrencyInstance();
System.out.println("Subtotal: "+ money.format(amount));
System.out.println("Discount: " + money.format(discount));
System.out.println("Total: " + money.format(total));
System.out.println("Tax: " + money.format(tax));
System.out.println("Tax+Total: " + money.format(taxedTotal));&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Y el resultado es:&lt;br /&gt;
&lt;br /&gt;
Subtotal: 100,05 €&lt;br /&gt;
Discount: 10,00 €&lt;br /&gt;
Total: 90,04 €&lt;br /&gt;
Tax: 4,50 €&lt;br /&gt;
Tax+Total: 94,55 €&lt;br /&gt;
&lt;br /&gt;
Si nos fijamos en la suma de total más impuestos hay una diferencia de 0.01 y si presentamos un desglose de precios&amp;nbsp;como este&amp;nbsp;a un usuario puede que este piense que hay algún error en el cálculo, le genere desconfianza y no haga la compra en el peor de los casos. Viendo los valores sin los redondeos que hace NumberFormat tenemos:&lt;br /&gt;
&lt;br /&gt;
Subtotal: 100.05&lt;br /&gt;
Discount: 10.005&lt;br /&gt;
Total: 90.045&lt;br /&gt;
Tax: 4.50225&lt;br /&gt;
Tax+Total: 94.54725&lt;br /&gt;
&lt;br /&gt;
Los redondeos que hace NumberFormat es HALF_EVEN por defecto, de modo que cuando un decimal está equidistante a las dos partes se redondea a la parte par por lo que con una precisión de dos decimales:&lt;br /&gt;
&lt;br /&gt;
Discount: 10.005 se redondea a 10.00&lt;br /&gt;
Total: 90.045 se redondea a 90.04&lt;br /&gt;
Tax: 4.50225 &amp;nbsp;se redondea a 4.50&lt;br /&gt;
Tax+Total: 94.54725 se redondea a 94.55&lt;br /&gt;
&lt;br /&gt;
En este caso se trata de un problema de redondeo pero ahora supongamos que tenemos una cantidad de 0,70 céntimos a la que no aplicamos un descuento pero si el procentaje de impuestos del 5%. Tendríamos:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;import java.text.NumberFormat;
double amount = 0.70;
double tax = amount * 0.05;
double taxedTotal = tax + amount;

NumberFormat money = NumberFormat.getCurrencyInstance();
System.out.println("Subtotal: "+ money.format(amount));
System.out.println("Tax: " + money.format(tax));
System.out.println("Tax+Total: " + money.format(taxedTotal));&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Subtotal: 0,70 €&lt;br /&gt;
Tax: 0,03 €&lt;br /&gt;
Tax+Total: 0,74 €&lt;br /&gt;
&lt;br /&gt;
Nos encontramos otra vez con la diferencia de 0.01. Vemos los valores sin redondear por NumberFormat:&lt;br /&gt;
&lt;br /&gt;
Subtotal: 0.7&lt;br /&gt;
Tax: 0.034999999999999996&lt;br /&gt;
Tax+Total: 0.735&lt;br /&gt;
&lt;br /&gt;
Aquí se ve que el resultado de ciertas operaciones aritméticas entre datos double (o  float) son almacenadas por una computadora con errores de precisión, 0.70 * 0.05 (debería ser 0.035).&lt;br /&gt;
&lt;br /&gt;
Para evitar estos errores debemos utilizar la clase BigDecimal que pemite almacenar números con una precisión en la práctica infinita en base 10, realizar los cálculos como los humanos esperan, en base diez, y hacer los redondeos de precisión. Aplicando una precisión de dos decimales a los números y usando BigDecimal tenemos:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;

RoundingMode RM = RoundingMode.HALF_EVEN;
BigDecimal amount = new BigDecimal("100.05"); 
BigDecimal discountPercent = new BigDecimal("0.10");
BigDecimal discount = amount.multiply(discountPercent).setScale(2, RM); 
BigDecimal total = amount.subtract(discount).setScale(2, RM);
BigDecimal taxPercent = new BigDecimal("0.05");
BigDecimal tax = total.multiply(taxPercent).setScale(2, RM);
BigDecimal taxedTotal = total.add(tax).setScale(2, RM);
NumberFormat money = NumberFormat.getCurrencyInstance(); 
System.out.println("Subtotal : " + money.format(amount));
System.out.println("Discount : " + money.format(discount));
System.out.println("Total : " + money.format(total)); 
System.out.println("Tax : " + money.format(tax)); 
System.out.println("Tax+Total: " + money.format(taxedTotal));&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Ahora los precios si están correctos:&lt;br /&gt;
&lt;br /&gt;
Subtotal : 100,05 €&lt;br /&gt;
Discount : 10,00 €&lt;br /&gt;
Total : 90,05 €&lt;br /&gt;
Tax : 4,50 €&lt;br /&gt;
Tax+Total: 94,55 €&lt;br /&gt;
&lt;br /&gt;
Para el otro caso en el que teníamos un error de precisión:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;

RoundingMode RM = RoundingMode.HALF_EVEN;
BigDecimal amount = new BigDecimal("0.70");
BigDecimal taxPercent = new BigDecimal("0.05");
BigDecimal tax = amount.multiply(taxPercent).setScale(2, RM);
BigDecimal taxedTotal = tax.add(amount).setScale(2, RM); 

NumberFormat money = NumberFormat.getCurrencyInstance(); 
System.out.println("Subtotal: "+ money.format(amount)); 
System.out.println("Tax: " + money.format(tax)); 
System.out.println("Tax+Total: " + money.format(taxedTotal));&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Subtotal: 0,70 €&lt;br /&gt;
Tax: 0,04 €&lt;br /&gt;
Tax+Total: 0,74 €&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://www.javamexico.org/blogs/luxspes/por_que_usar_bigdecimal_y_no_double_para_calculos_aritmeticos_financieros"&gt;http://www.javamexico.org/blogs/luxspes/por_que_usar_bigdecimal_y_no_double_para_calculos_aritmeticos_financieros&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://speleotrove.com/decimal/decifaq1.html#tzeros"&gt;http://speleotrove.com/decimal/decifaq1.html#tzeros&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.mkyong.com/java/how-do-calculate-monetary-values-in-java-double-vs-bigdecimal/"&gt;http://www.mkyong.com/java/how-do-calculate-monetary-values-in-java-double-vs-bigdecimal/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://blogs.oracle.com/CoreJavaTechTips/entry/the_need_for_bigdecimal"&gt;http://blogs.oracle.com/CoreJavaTechTips/entry/the_need_for_bigdecimal&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://stackoverflow.com/questions/7539/please-explain-the-use-of-java-math-mathcontext/7561#7561"&gt;http://stackoverflow.com/questions/7539/please-explain-the-use-of-java-math-mathcontext/7561#7561&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://en.wikipedia.org/wiki/Floating_point"&gt;http://en.wikipedia.org/wiki/Floating_point&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-8003222930198638376?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/tWwZa_8hizU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/8003222930198638376/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2012/01/errores-de-precision-redondeo-y.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8003222930198638376?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8003222930198638376?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/tWwZa_8hizU/errores-de-precision-redondeo-y.html" title="Errores de precisión, redondeo y representación con float y double" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2012/01/errores-de-precision-redondeo-y.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIMQ38-fSp7ImA9WhRUEUs.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-2612813524779796078</id><published>2012-01-01T15:19:00.001+01:00</published><updated>2012-01-21T17:16:22.155+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-21T17:16:22.155+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="otros" /><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 2012</title><content type="html">Empieza un nuevo año y una de las cosas que primero hacemos es ver y obtener un calendario para poner encima de nuestra mesa. Hace unos días vi un &lt;a href="http://paraisolinux.com/2-calendarios-de-ubuntu-para-imprimir-del-2012/?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+paraisoLinux+%28Paraiso+Linux%29"&gt;calendario tematizado con Ubuntu&lt;/a&gt;. En un rato que he sacado he tenido tiempo de crear uno basado en Arch Linux y traducido al español. Así que aquí lo tenéis listo y esperando para imprimir, construirlo y ponerlo encima de vuestra mesa. En el paquete tenéis la versión SVG, PNG y PDF (200 KiB) lo podéis &lt;a href="https://docs.google.com/open?id=0B4yquUFv3csrNmJjMDQ5ODYtYzk4Yi00MGM4LWE1MDItMmJiNzI2OGZhZmY5"&gt;obtener mediante este enlace&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/-lHmvpG811Xw/TwBqehbFy-I/AAAAAAAABCo/sxILVMr3dug/s1600/arch+linux+calendar+%25282012%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img alt="Calendario Arch Linux 2012" border="0" height="400" src="http://4.bp.blogspot.com/-lHmvpG811Xw/TwBqehbFy-I/AAAAAAAABCo/sxILVMr3dug/s400/arch+linux+calendar+%25282012%2529.png" title="Calendario Arch Linux 2012" width="308" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Si le cogeís gusto a &lt;strike&gt;la papiroflexia&lt;/strike&gt;&amp;nbsp;los modelos de papel tal vez os interese obtener este &lt;a href="http://elblogdepicodev.blogspot.com/2010/03/arch-linux-cheat-cube.html"&gt;Cheat Cube tambén para nuestra distribución&lt;/a&gt;. Ahora Tux y el cheat cube van a tener un nuevo compañero :). En cuanto pueda imprimirlo le saco unas fotos.&lt;br /&gt;
&lt;br /&gt;
¡Que empecéis bien el año!&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://paraisolinux.com/2-calendarios-de-ubuntu-para-imprimir-del-2012/?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+paraisoLinux+%28Paraiso+Linux%29"&gt;Calendarios de ubuntu para imprimir del 2012&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://spreadubuntu.org/es/node/751"&gt;http://spreadubuntu.org/es/node/751&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-2612813524779796078?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/nXTvIcyU6Z0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/2612813524779796078/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2012/01/calendario-arch-linux-2012.html#comment-form" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2612813524779796078?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2612813524779796078?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/nXTvIcyU6Z0/calendario-arch-linux-2012.html" title="Calendario Arch Linux 2012" /><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/-lHmvpG811Xw/TwBqehbFy-I/AAAAAAAABCo/sxILVMr3dug/s72-c/arch+linux+calendar+%25282012%2529.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2012/01/calendario-arch-linux-2012.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4BRnk5cSp7ImA9WhRXFks.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-4395688641744365394</id><published>2011-12-23T18:56:00.000+01:00</published><updated>2011-12-23T19:12:37.729+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-23T19:12:37.729+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>Feliz navidad y próspero 2012</title><content type="html">Ha pasado un año completo para El blog de pico.dev y en febrero del 2012 este blog cumplirá el segundo aniversario. A punto de finalizar el 2011 llega el momento de hacer balance de lo que ha dado de si este año. En este 2011 he publicado 43 entradas, unas pocas menos que una media de una a la semana que me he fijado como objetivo desde hace un tiempo. Aunque aún el año no ha acabado este blog ha tenido cerca de 34.271 visitas, 25.251 visitantes y 55.280 páginas vistas, dicho así parecen muchas pero son «solo» cerca de unas 90-150 visitas diarias. Aún así son considerablemente más que el año 2010 (12.970 visitas, 9.758 visitantes y 20.946 páginas vistas) lo que es casi triplicar el tráfico del año anterior, el hecho de ir acumulando contenido en el blog se nota a medida que pasa el tiempo aunque muy lentamente. Seguro que otros blogs que publican cientos de entradas al cabo del año y tienen a varios usuarios publicando en él tienen bastantes más visitas, aunque mi objetivo no es publicar un gran número de entradas del estilo «Como instalar/actualizar el programa X por cada versión menor (X.X.1) en Ubuntu (u otra distribución)» o «Publicada la versión menor X.X.1 del programa X» que creo que aportan más bien poco sino escribir entradas que compartan alguna información más útil.&lt;br /&gt;
&lt;br /&gt;
En cuanto a la temática de las entradas he escrito más sobre programación que lo que hice en el 2010 y eso creo que va a seguir siendo así aunque no dejaré de escribir de vez en cuando alguna más orientada a (Arch) Linux y software libre.&lt;br /&gt;
&lt;br /&gt;
Si habéis llegado o suscrito recientemente a mi blog (y si aún no lo habéis hecho estáis invitados) os pongo aquí la hemeroteca de las entradas que he escrito durante este año:&lt;br /&gt;
&lt;br /&gt;
Programación:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/02/unir-apache-httpd-y-tomcat-mediante-un.html"&gt;Unir Apache HTTPD y Tomcat mediante un reverse proxy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/02/enviar-correos-mediante-java-mail.html"&gt;Enviar correos electrónicos mediante Java Mail&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/02/implementacion-de-un-comparator.html"&gt;Implementación de un Comparator genérico en Java con ayuda de Groovy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/05/servidor-web-ligero-cherokee.html"&gt;Servidor web ligero Cherokee, instalación e integración con PHP en Arch Linux&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/06/elegir-herramientas-para-un-proyecto.html"&gt;Elegir herramientas para un proyecto Java&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/06/instalar-symfony-y-cherokee-en-arch.html"&gt;Instalar Symfony y Cherokee en Arch Linux&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/06/hola-mundo-con-symfony-2-y-cherokee.html"&gt;Hola mundo con Symfony 2 y Cherokee&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/internacionalizacion-lenguajes-con.html"&gt;Internacionalización a lenguajes con diferentes formas plurales en Java&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/seleccionar-el-lenguaje-locale-segun-el.html"&gt;Seleccionar el lenguaje (locale) según el dominio en Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/comparar-cadenas-en-java-ignorando.html"&gt;Comparar cadenas en Java ignorando acentos&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/integracion-continua-con-jenkins.html"&gt;Integración continua con Jenkins&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/10/configuracion-de-entorno-en-java-con.html"&gt;Configuración de entorno en Java con ayuda de Groovy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/10/ejemplo-sencillo-con-javacc-de-un.html"&gt;Ejemplo sencillo con JavaCC de un analizador léxico y sintáctico&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/11/formatear-precios-con-simbolo-de-moneda.html"&gt;Formatear precios con símbolo de moneda en Java&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/11/internacionalizacion-i18n-de-campos-con.html"&gt;Internacionalización (i18n) de campos con Hibernate&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/12/convenciones-para-los-literales-en.html"&gt;Convenciones para los literales en archivos .properties en Java&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Tapestry:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/01/componente-lista-para-tapestry-5.html"&gt;Componente cache para Tapestry 5&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/01/componente-cache-para-tapestry-5.html"&gt;Componente lista para Tapestry 5 (paginable y anidable)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/07/usar-apache-tapestry-5-con-groovy-u.html"&gt;Usar Apache Tapestry 5 con Groovy (u otros lenguajes de la JVM)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/motivos-para-elegir-apache-tapestry.html"&gt;Motivos para elegir el framework Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/peticiones-ajax-en-tapestry.html"&gt;Peticiones Ajax en Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/10/peticiones-ajax-en-tapestry-ii.html"&gt;Peticiones Ajax en Tapestry (II)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/componente-ajaxspinner-para-apache.html"&gt;Componente AjaxSpinner para Tapestry 5&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
(Arch) Linux:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/03/instalar-plymouth-como-arranque-grafico.html"&gt;Instalar Plymouth como arranque gráfico en Arch Linux&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/04/poner-en-color-pacman-y-yaourt.html"&gt;Poner en color pacman y yaourt&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/04/obtener-informacion-del-sistema-en.html"&gt;Obtener información del sistema en Linux (con inxi, lspci y hwinfo)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/05/anadir-marcas-de-agua-con-imagemagick.html"&gt;Añadir marcas de agua a imágenes con ImageMagick&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/07/motivos-para-elegir-la-distribucion.html"&gt;Motivos para elegir la distribución Arch Linux&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/11/personalizar-gnome-iconos-temas.html"&gt;Personalizar GNOME (iconos, temas, extensiones, opciones)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/12/arte-usando-texto-ascii-con-figlet.html"&gt;Arte usando texto ASCII con FIGlet&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/07/video-promocional-sobre-arch-linux.html"&gt;Vídeo promocional sobre Arch Linux&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/05/impuesto-windows.html"&gt;El impuesto Windows&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/12/como-seria-si-microsoft-apple-y-linux.html"&gt;Como sería si Microsoft, Apple y Linux...&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Otros:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/07/un-dia-en-la-euskal-encounter-19.html"&gt;Un día en la Euskal Encounter 19&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Dicho lo cual solo me queda daros las gracias por ser parte de las visitas de este blog y desearos...&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;img border="0" src="http://2.bp.blogspot.com/-E7XmF7u1DVw/TvTDwtxgSeI/AAAAAAAABCU/a6KTv1vfSyc/s640/feliz-navidad-2011.jpg" width="550" /&gt;&lt;/div&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://laventanamuerta.net/haz-llegar-la-navidad-a-tu-escritorio-con-los-fondos-navidenos-de-tux/"&gt;http://laventanamuerta.net/haz-llegar-la-navidad-a-tu-escritorio-con-los-fondos-navidenos-de-tux/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.klowner.com/wallpaper/"&gt;http://www.klowner.com/wallpaper/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.youtube.com/watch?v=WdyplU0RVk8"&gt;http://www.youtube.com/watch?v=WdyplU0RVk8&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-4395688641744365394?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/IX3KQ7ZhlPQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/4395688641744365394/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/12/feliz-navidad-y-prospero-2012.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4395688641744365394?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4395688641744365394?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/IX3KQ7ZhlPQ/feliz-navidad-y-prospero-2012.html" title="Feliz navidad y próspero 2012" /><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/-E7XmF7u1DVw/TvTDwtxgSeI/AAAAAAAABCU/a6KTv1vfSyc/s72-c/feliz-navidad-2011.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/12/feliz-navidad-y-prospero-2012.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIMQ38-eyp7ImA9WhRUEUs.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-2160659033704026519</id><published>2011-12-17T20:08:00.000+01:00</published><updated>2012-01-21T17:16:22.153+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-21T17:16:22.153+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="otros" /><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="planeta código" /><title>Arte usando texto ASCII con FIGlet</title><content type="html">Seguramente hayas visto en algunos sitios una figura o texto formado por caracteres ASCII, probablemente no hayan sido creadas a mano con la inspiración de un artista, hay herramientas incluso usables desde la web que permiten generarlas. &lt;a href="http://www.figlet.org/"&gt;FIGlet&lt;/a&gt; es una de ellas que permite crear una figura ASCII a partir de un texto. Su instalción en Arch Linux es tan simple como instalar el paquete figlet con:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;$ yaourt -S figlet&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
En la propia página de figlet podemos encontrar enlaces a varios conjuntos de fuentes a aplicar al texto que queramos generar. También podemos ver &lt;a href="http://www.figlet.org/fontdb.cgi"&gt;algunos ejemplos&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
FIGlet tiene &lt;a href="http://www.figlet.org/figlet-man.html"&gt;más opciones&lt;/a&gt; pero la más habitual tal vez sea indicar la fuente a usar y el mensaje del que queremos su representación en arte ASCII.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;$ figlet -w 200 "El blog de pico.dev"
 _____ _   _     _                   _              _               _            
| ____| | | |__ | | ___   __ _    __| | ___   _ __ (_) ___ ___   __| | _____   __
|  _| | | | '_ \| |/ _ \ / _` |  / _` |/ _ \ | '_ \| |/ __/ _ \ / _` |/ _ \ \ / /
| |___| | | |_) | | (_) | (_| | | (_| |  __/ | |_) | | (_| (_) | (_| |  __/\ V / 
|_____|_| |_.__/|_|\___/ \__, |  \__,_|\___| | .__/|_|\___\___(_)__,_|\___| \_/  
                         |___/               |_| 

$ figlet -f letters.flf -w 200 "El blog de pico.dev"

EEEEEEE lll  bb      lll                      dd                 iii                        dd                
EE      lll  bb      lll  oooo   gggggg       dd   eee   pp pp         cccc  oooo           dd   eee  vv   vv 
EEEEE   lll  bbbbbb  lll oo  oo gg   gg   dddddd ee   e  ppp  pp iii cc     oo  oo      dddddd ee   e  vv vv  
EE      lll  bb   bb lll oo  oo ggggggg  dd   dd eeeee   pppppp  iii cc     oo  oo ... dd   dd eeeee    vvv   
EEEEEEE lll  bbbbbb  lll  oooo       gg   dddddd  eeeee  pp      iii  ccccc  oooo  ...  dddddd  eeeee    v    
                                 ggggg                   pp
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Los textos ASCII generados por FIGlet nos pueden ser de utilidad en los sitios en los que solo podamos usar caracteres de texto como en correos electrónicos que no lleven formato html o en los archivos de log de una aplicación.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://www.figlet.org/"&gt;http://www.figlet.org/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://patorjk.com/software/taag/"&gt;http://patorjk.com/software/taag/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.network-science.de/ascii/"&gt;http://www.network-science.de/ascii/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-2160659033704026519?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/e5hp88Ypw38" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/2160659033704026519/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/12/arte-usando-texto-ascii-con-figlet.html#comment-form" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2160659033704026519?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2160659033704026519?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/e5hp88Ypw38/arte-usando-texto-ascii-con-figlet.html" title="Arte usando texto ASCII con FIGlet" /><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><thr:total>1</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/12/arte-usando-texto-ascii-con-figlet.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEYGQ30-eSp7ImA9WhRQFUs.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1247059018999880037</id><published>2011-12-09T19:41:00.001+01:00</published><updated>2011-12-11T01:08:42.351+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-11T01:08:42.351+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software privativo" /><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="Apple" /><category scheme="http://www.blogger.com/atom/ns#" term="Microsoft" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Como sería si Microsoft, Apple y Linux...</title><content type="html">¿Como sería si Microsoft, Apple y Linux se decicasen a la industria automovilística y vendiesen motores y coches conociendo sus políticas y formas de comercializar su software y productos?&lt;br /&gt;
&lt;br /&gt;
set small_troll_mode=on&lt;br /&gt;
&lt;br /&gt;
Empecemos por &lt;b&gt;Microsoft&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Microsoft" border="0" height="75" src="http://1.bp.blogspot.com/-XdqPJUzoXGQ/TuJcYIW8UkI/AAAAAAAABB8/nw_ryoLf4NY/s320/microsoft.png" title="Microsoft" width="320" /&gt;&lt;/div&gt;
Algunos modelos de sus motores son conocidos y recordados por provocar fallos en el coche que son instalados [1], algunos no más molestos como que las ruedas se desinchan de vez en cuando o el aire acondicionado no funciona a veces si a la vez tienes puesta la radio y algunos graves como que el motor se apaga de repente o deja de frenar cuando lo necesitas que provocan pequeños accidentes y pequeñas perdidas de memoria en el conductor a los que se les queda la cara de un tono azulado después de los hechos por el susto.&lt;br /&gt;
&lt;br /&gt;
Aproximádamente cada segundo martes del mes [2] tienes que llevar el coche al taller para hacer una revisión de algunas partes del coche por fallos de seguridad detectados en diversas partes del mismo, no sabes muy bien que van a revisar pero oye si lo dice Microsoft por algo será. Otros problemas de seguridad conocidos y explotados por los ladrones para acceder al interior no son resueltos pasados meses e incluso años siempre a criterio de Microsoft, ellos saben lo que es importante y bueno para sus usuarios.&lt;br /&gt;
&lt;br /&gt;
Los coches vienen con un montón de extras inútiles [3] que no quieres como una alerón hortera, una luces de neon en los bajos o un sistema de alarma que deja de funcionar a los 30 días si no pagas la licencia. Todo ello hace que el coche nuevo y recien salido de fábrica circule a una velocidad máxima de 80 km/h cuando debería alcanzar los 120 km/h y a veces sin ningún motivo aparente se reduzca a 50 km/h pasados unos meses.&lt;br /&gt;
&lt;br /&gt;
No puedes abrir el capó del coche para para ver como es el motor, y por tanto no puedes cambiarle de aceite, la batería o algunas piezas para conseguir una mayor potencia, fiabilidad, seguridad o menor consumo de combustible.&lt;br /&gt;
&lt;br /&gt;
Las administraciones públicas que tienen enormes flotas de automóviles comprados a Microsoft [4] se gastan cada año millones de euros en renovar licencias para usarlos, ya que el coche en realidad no lo compran solo les da un licencia de uso. Pueden comprar coches con motor Linux que algunos no tienen costes de licencias pero los funcionarios están acostumbrados a los coches con motor Windows, es el que usan como particulares.&lt;br /&gt;
&lt;br /&gt;
Por lo visto debe de haber una forma de duplicar un motor rápidamente, las personas lo utilizan para instalarle a sus coches la última versión del motor Windows que siempre dicen trae nuevas prestaciones. Saben que esto no es legal por la licencia de uso del motor y les llaman piratas, pero a la vez Microsoft lo consiente, sabe que cuando vayan a sus trabajos sus empresas tendrán que pagar ya que no estarán acostuambrados a ningún otro tipo de motor. Sino puede obligarles a comprar la licencia como particulares al menos los convierte en adictos a sus motores, piensan que es mejor que usen uno de ellos pirateado que uno de la competencia [5], y así están los funcionarios de muchas las administraciones públicas.&lt;br /&gt;
&lt;br /&gt;
En alguna ocasión el navegador GPS de Microsoft venía ya instalado en el coche junto con el motor. Sin embargo, la Unión Europera a partir de una denuncia del fabricante de navegadores Opera [6] dictaminó que esto era un abuso de posición dominante ya que impedía al resto de fabricantes de GPS colocar sus navegadores y dificultaba la libre competencia. Microsoft argumentaba que el navegador GPS era parte inseparable de su motor y que sin él en no funcionaba. Finalmente, Microsoft se vió obligada a darle al usuario la posibilidad de seleccionar su navegador GPS en una Ballot Screen [7], ya estaba escarmentada de anteriores multas por valor de 500 millones de euros por anteriores litigios [8].&lt;br /&gt;
&lt;br /&gt;
Pasados 5 años, con suerte, tienes que comprar otro coche porque el que que tienes ya no puede cargar con los trastos que llevas en él, no sabes por qué ya que los utilizas para hacer lo mismo que hace 5 años solo son un poco más modernos, te han comentado que puede ser debido a la obsolescencia programada. Tal vez solo quieres la carrocería que ya tienes el motor de tu anterior coche que es antiguo pero que todavía puede funcionar en el nuevo o tal vez te plantes instalar un motor basado en Linux ya que conoces alguna distribución que te lo dá sin ningún coste, ya estas cansado de los defectos de los motores Windows, el comercial sorprendido te dirá que eso no es posible que a ellos los fabricantes de coches no les venden solo carrocerias, ya les dan el motor de Windows preinstalado y así es como lo venden, a esto algunas personas lo conocen como el impuesto Windows.&lt;br /&gt;
&lt;br /&gt;
El turno de &lt;b&gt;Apple&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Apple" border="0" height="200" src="http://4.bp.blogspot.com/-3MJB1pCLYQs/TuJcVZvonJI/AAAAAAAABB0/_idkN6Gi-g0/s200/apple.png" title="Apple" width="167" /&gt;&lt;/div&gt;
Al contrario que Microsoft, Apple no solo fabrica motores sino también la carrocería y tampoco podras adquirirlos por separado. La producción se ha deslocalizado a China ya que allí hay mano de obra más barata, abundante y los trabajadores de esas plantas tiene menos derechos laborales que los trabajadores occidentales. Sin embargo, cuando sale algún caso de que algún empleado de esas fábricas llega a suicidarse [1] por las condiciones en que se encuentra, en Apple, dicen que van a cancelar relaciones contractuales si los contratistas no ponen remedio en un intento de que el asunto no afecte a su marca [1].&lt;br /&gt;
&lt;br /&gt;
Apple no fabrica coches de bajo coste, según ellos no saben hacerlo sin que sean una basura [2], (¡tragaros eso fabricantes de utilitarios PC!),&amp;nbsp;asunto arreglado ya tienen una razón para vender sus coches &amp;nbsp;esos precios,&amp;nbsp;a pesar de utilizar las mismas piezas, son sensiblemente mas caros que los coches PC. A quien no le importa pagar el precio de un coche Mac no le importa sacar de nuevo y a menudo la tarjeta de crédito para pagar el precio que tienen sus accesorios y para pagar los añadidos de la nueva App Store, motivo por el cual esta tiene más éxito que el mismo concepto en otras marcas.&lt;br /&gt;
&lt;br /&gt;
Cada cierto tiempo sacan nuevas actualizaciones para sus motores con un precio de tan solo X con 99, tipica regla comercial para fijar precios y hacer pensar a los clientes que estan pagando menos en un intento por confundirles y demostrando que no les importa contar mentiras piadosas, parece que funciona.&lt;br /&gt;
&lt;br /&gt;
Tampoco puedes abrir el capo para ver como es el motor ni modificarlo ni puedes saber si te han puesto un sistema de segimiento y rastreo CarrierIQ con el que puedan conocer en que sitios has estado o analizar y recopilar información tuya sin tu consentimiento ni saber con que fines [3]. Cuando los alarmados conductores Mac se informan de la existencia de dichos dispositivos instalados en sus vehículos les comentan que en el próximo modelo dejarán de utilizarlos, prometen hacer borrón, cuenta nueva y asunto arreglado, aquí no ha pasado nada ¡estos no son los androides que andáis buscando!. Algunas personas tachadas de&amp;nbsp;poco menos que locos&amp;nbsp;ya advertían de que estas funciones malévolas en motores privativos podían estar presentes [4].&lt;br /&gt;
&lt;br /&gt;
Dicen que sus productos son respetuosos con el medio ambiente, solo utilizan piezas que lo sean, son eléctricos e híbridos nada de utilizar la obsoleta y contaminante tecnología de combustión. Sus motores son lo último y son productos deseados, su maquinaria de marketing se encarga de hacer que asi sea. Tienen una carrocería cuidada pero solo en dos colores, negro y blanco, este último les costó sacarlo al mercado varios meses más tarde que el primero parece ser que por un problema en los focos [5].&lt;br /&gt;
&lt;br /&gt;
Apple es distinta, en los lanzamientos de sus productos algunos clientes pueden pasarse una noche esperando en la puerta del concesionario a que abran al día siguiente para gastarse su dinero y comprar lo último de la marca [6]. Para que no decaiga el ánimo los empleados del concesionario los reciben con aplausos y vitores cuando entran.&lt;br /&gt;
&lt;br /&gt;
Ahora &lt;b&gt;Linux&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Linux" border="0" height="200" src="http://4.bp.blogspot.com/-gf0CBdHQ0EI/TQJrCzG9OZI/AAAAAAAAAiY/NcJAFNmleAw/s200/tux.png" title="Linux" width="169" /&gt;&lt;/div&gt;
Los motores Linux son libres y podrás utilizarlos con cualquier propópsito. Su licencia te da el derecho a obtener sus planos, y si tienes los conocimientos puedes estudiarlos, modificarlos y redistribuir tu mejoras con tus vecinos para ayudarles, estas son las 4 libertades básicas [1]. Algunas personas han logrado proporcionar la mayor potencia, fiabilidad, seguridad o menor consumo de combustible que no se puede hacer con motores Windows o Max OS X. Con los accesorios en su mayoría tambien libres pasa otro tanto de lo mismo.&lt;br /&gt;
&lt;br /&gt;
Han demostrado su fiabilidad y ser muy versátiles y se pueden instalar además de coches, en otro tipo de vehículos como motos, camiones, barcos y aviones e incluso en micromotos como Kutxashares. Pueden utilizar no solo válvulas x86 sino también de las marcas ARM, MIPS, OpenRisc, PowerPC y otras menos conocidas [2].&lt;br /&gt;
&lt;br /&gt;
No existen concesionarios Linux y no lo encontrarás en el resto de concesionarios PC, se piden por internet y en la mayoria de los casos su coste es nulo, a pesar de ello la mayoría de personas PAsan DE FOllones y se quedan con lo que les dán preinstalado aceptando sus defectos. Su negocio no se basa&amp;nbsp;principalmente&amp;nbsp;en vender licencias sino servicios, como &lt;a href="http://www.redhat.com/"&gt;RedHat&lt;/a&gt;, &lt;a href="http://www.ibm.com/us/en/"&gt;IBM&lt;/a&gt;&amp;nbsp;y &lt;a href="http://www.canonical.com/"&gt;Canonical&lt;/a&gt;. Dado que cualquier persona puede proponer mejoras construyen comunidades, el avance conseguido por esas comunidades luego es aplicado a sus productos comerciales y el avance en los productos comerciales acaba llegando también a las comunidades de modo que todo el mundo se vea beneficiado. Alrededor de los motores Linux y sus accesorios se crean talleres mecánicos y puestos de trabajo localizados [3] con el objetivo de prestar esos servicios en su ámbito local.&lt;br /&gt;
&lt;br /&gt;
El reciente concepto de App Store y Market para Apple y Microsoft es algo que ya tenian los distribuidores Linux desde hace ya mucho tiempo.&lt;br /&gt;
&lt;br /&gt;
Estos motores tampoco están exentos de fallos y a veces a sus coches también se les desinchan las ruedas pero por lo general una vez conocidos son resueltos más rápidamente que en los coches Microsoft y Apple. Algunos fabricantes de accesorios mecánicos no dan soporte para estos motores y a veces no funcionan correctamente aunque los más comunes ya no suelen presentar problemas, no es como en los años 90 pero la leyenda urbana sigue extendida.&lt;br /&gt;
&lt;br /&gt;
Algunas versiones de los distribuidores requieren que los usuarios monten las piezas ellos mismos como en &lt;a href="http://www.archlinux.org/"&gt;Arch Linux&lt;/a&gt; y &lt;a href="http://www.gentoo.org/"&gt;Gentoo&lt;/a&gt;, otros distribuidores lo ponen tan fácil o más que no tienen nada que envidiar a los coches Microsoft, es el caso de &lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt;, &lt;a href="http://linuxmint.com/"&gt;Linux Mint&lt;/a&gt; y &lt;a href="http://fedoraproject.org/es/"&gt;Fedora&lt;/a&gt;. Tener esta amplia variedad de modelos de motores Linux hace que a alguna gente le cueste decidirse por uno, tratan de buscar «el mejor», pero se equivocan, en Linux no hay «el mejor» quizá sí el mas adecuado para cada tipo de conductor. Para los que tienen el primer motor Linux posiblemente las opciones más adecuadas serían&amp;nbsp;&lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt;,&amp;nbsp;&lt;a href="http://linuxmint.com/"&gt;Linux Mint&lt;/a&gt;,&amp;nbsp;&lt;a href="http://fedoraproject.org/es/"&gt;Fedora&lt;/a&gt;&amp;nbsp;o &lt;a href="http://es.opensuse.org/"&gt;openSUSE&lt;/a&gt;.&amp;nbsp;Para los usuarios que ya llevan un tiempo usándolos quizá les parezca más adecuada para sus necesidades &lt;a href="http://www.archlinux.org/"&gt;Arch Linux&lt;/a&gt;&amp;nbsp;aunque hay muchos otros modelos [4].&lt;br /&gt;
&lt;br /&gt;
Pasado el tiempo si tratas bien el coche y no lo metes por cualquier carrtera no notarás una degradación de prestaciones y funcionará a los mismos 120 km/h del primer día.&lt;br /&gt;
&lt;br /&gt;
set small_troll_mode=off&lt;br /&gt;
&lt;br /&gt;
Por suerte ni Microsoft, ni Apple, ni Linux se dedican a la fabricación de motores ni coches pero piensa si lo hicieran si aceptarías que instalasen en tu coche un dispositivo de seguimiento sin que te informasen de ello o si no te parecería ridículo que hubiese gente esperando una noche en la puerta de un concesionario para comprar un nuevo modelo de Renault o si tu coche recien comprado te viene con un motón de accesorios que no quieres o si... tal vez tampoco los coches y los ordenadores no sean lo mismo pero... ¿si algunas de estas cosas no las aceptarías en tu coche u otros ámbitos por que las aceptas en tu ordenador? Tú decides.&lt;br /&gt;
&lt;br /&gt;
Artículos relacionados:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/05/impuesto-windows.html"&gt;El impuesto Windows&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/03/de-windows-arch-linux.html"&gt;De Windows a Arch Linux&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/05/guia-instalacion-arch-linux-parte-i.html"&gt;Guía instalación Arch Linux (Parte I, instalación base)&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://www.microsoft.com/en-us/default.aspx"&gt;Microsoft&lt;/a&gt;&lt;br /&gt;
[1] Buscar: Windows Millenium y Windows Vista&lt;br /&gt;
[2] Buscar: windows martes de parches&lt;br /&gt;
[3] Buscar: windows programas preinstaldos&lt;br /&gt;
[4] Buscar: windows administracion pública&lt;br /&gt;
[5] Buscar: Las versiones pirateadas de Windows frenan el avance de Linux&lt;br /&gt;
[6] Buscar: windows explorer denuncia opera&lt;br /&gt;
[7] Buscar: windows ballot screen&lt;br /&gt;
[8] Buscar: microsoft multa union europea&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.apple.com/"&gt;Apple&lt;/a&gt;&lt;br /&gt;
[1] Buscar: apple foxconn suicidios&lt;br /&gt;
[2] Buscar: apple ordenadores basura 500&lt;br /&gt;
[3] Buscar: apple carrieriq&lt;br /&gt;
[4] Buscar: richard stallman&lt;br /&gt;
[5] Buscar: apple iphone blanco retrasado&lt;br /&gt;
[6] Buscar: apple colas tiendas&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://www.linux.com/"&gt;Linux&lt;/a&gt;&lt;br /&gt;
[1] Buscar: libertades software libre&lt;br /&gt;
[2] Buscar: linux arquitecturas soportadas&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;/div&gt;
[3] Buscar: empresas software libre euskadi&lt;br /&gt;
[4] Buscar: linux distribuciones&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-1247059018999880037?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/_ll_k7wOjgE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1247059018999880037/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/12/como-seria-si-microsoft-apple-y-linux.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1247059018999880037?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1247059018999880037?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/_ll_k7wOjgE/como-seria-si-microsoft-apple-y-linux.html" title="Como sería si Microsoft, Apple y Linux..." /><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/-XdqPJUzoXGQ/TuJcYIW8UkI/AAAAAAAABB8/nw_ryoLf4NY/s72-c/microsoft.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/12/como-seria-si-microsoft-apple-y-linux.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUGQ3wzeSp7ImA9WhRQEUk.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-4646417253233345498</id><published>2011-12-02T20:11:00.001+01:00</published><updated>2011-12-06T04:47:02.281+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-06T04:47:02.281+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Convenciones para los literales en archivos .properties en Java</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;br /&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;/div&gt;
La internacionalización (i18n) y la localización (i10n) en las aplicaciones Java ya sean de escritorio o aplicaciones web se suelen hacer a través de archivos .properties obtenidos a partir de la clase &lt;a href="http://download.oracle.com/javase/7/docs/api/java/util/ResourceBundle.html"&gt;ResourceBundle&lt;/a&gt; que contienen los literales para cada uno de los idiomas que soporta la aplicación, a cada literal se le asocia una clave que será el medio por el cual se identificará a la hora de recuperarlos.&lt;br /&gt;
&lt;br /&gt;
Un extracto de un archivo típico suele tener el siguiente aspecto:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;...
#Combos
comboDepartamentos=Todos los departamentos
comboAreas=Todas las áreas
comboRepositorios=Todos los repositorios
comboAplicaciones=Todas las aplicaciones
comboModulos=Todos los módulos
comboProgramas=Todos los programas
...
#Mantenimiento de Repositorios
mr.aceptar=Aceptar
mr.buscar=Buscar
mr.cancelar=Cancelar
mr.confirmarEliminar=Los repositorios seleccionados pueden tener información relacionada que será eliminada en cascada. ¿Desea eliminar los repositorios seleccionados y la información relacionada?
mr.registroEliminado=El registro ha sido eliminado por otro usuario.
mr.validacion.nombreExiste=Existe otro repositorio con el mismo nombre.
...
#Mantenimiento de Aplicaciones
ma.aceptar=Aceptar
ma.buscar=Buscar
ma.cancelar=Cancelar
...
#Mantenimiento de Módulos
mm.aceptar=Aceptar
mm.buscar=Buscar
mm.confirmarEliminar=Los módulos seleccionados pueden tener información relacionada que será eliminada en cascada. ¿Desea eliminar los módulos seleccionados y la información relacionada?
...&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Como se puede observar las claves de los literales de cada pantalla están identificados por un prefijo (mr, ma, mm) con el objetivo de agrupar los de cada pantalla, algunos no tienen prefijo como los de la sección #Combos. A los literales muy largos como mr.confirmarEliminar y mm.confirmarEliminar se les asigna una clave más corta que el propio literal y que tendremos que asignar para identificarlo.&lt;br /&gt;
&lt;br /&gt;
Estas convenciones tienen algunos problemas. Uno de ellos es que hay literales que son muy comunes en la aplicación y estarán varias veces repetidos como Aceptar, Buscar, Cancelar lo que implica que los traductores tendrán que tarducirlo varias veces y a los programadores mantener mayor número de literales, además de ralentizar el trabajo ¿tiene sentido pedir a los traductores traducir el mismo literal varias veces? No. Los literales largos como mr.confirmarEliminar y mm.confirmarEliminar tienen el problema de que sus claves son poco representativas de su literal siendo vistos en el archivo de código fuente donde se use, lo que hace que leerlo sea poco significativo y representativo de los textos que luego se muestran.&lt;br /&gt;
&lt;br /&gt;
Para solventar estos problemas y después de varios años de experiencia trabajando con archivos de literales en Java prefiero seguir otras convenciones. Según estas los archivos tendrían el siguiente aspecto:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;...
Aceptar=Aceptar
Buscar=Buscar
Cancelar=Cancelar
...
El_registro_ha_sido_eliminado_por_=El registro ha sido eliminado por otro usuario.
Existe_otro_repositorio_con_=Existe otro repositorio con el mismo nombre.
Los_repositorios_seleccionados_pueden_tener_información_relacionada_=Los repositorios seleccionados pueden tener información relacionada que será eliminada en cascada. ¿Desea eliminar los repositorios seleccionados y la información relacionada?
...
Los_módulos_seleccionados_pueden_tener_información_relacionada_=Los módulos seleccionados pueden tener información relacionada que será eliminada en cascada. ¿Desea eliminar los módulos seleccionados y la información relacionada?
...
#Combos
Todas_las_aplicaciones=Todas las aplicaciones
Todas_las_areas=Todas las áreas
Todos_los_departamentos=Todos los departamentos
Todos_los_modulos=Todos los módulos
Todos_los_programas=Todos los programas
Todos_los_repositorios=Todos los repositorios
...&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Como se puede ver para los literales cortos la clave será el propio literal en nuestro idioma nativo sin ñ o tildes, con esto al leer el archivo de código fuente sabremos cual es el texto exacto que se mostrará y no tendremos que estar inventándonos constantemente identificadores para los literales. Los literales cortos pero formados por varias palabras tendrán un «_» para representar el espacio. Las claves de los literales largos son el literal acortado con suficientes palabras para que no se repitan, les añado un «_» al final para saber que se ha acortado, el objetivo de acortarlo es que con unas cuantas palabras ya podemos hacernos una idea del texto que se muestra. Además los literales están ordenados alfabéticamente por la clave para encontrarlos más fácilmente. No digo que estas reglas no puedan tener excepciones para algunos casos pero siguiéndolas para la mayoría evitaremos que según vaya pasando el tiempo estos archivos se conviertan en un problema de mantenimiento, cuando en una aplicación tenemos 2000 o más literales, créeme, puede llegar a serlo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-4646417253233345498?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/__o0mlgEk3Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/4646417253233345498/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/12/convenciones-para-los-literales-en.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4646417253233345498?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4646417253233345498?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/__o0mlgEk3Q/convenciones-para-los-literales-en.html" title="Convenciones para los literales en archivos .properties en Java" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/12/convenciones-para-los-literales-en.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8BRXg-eCp7ImA9WhRRFkk.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-3446299687808840618</id><published>2011-11-23T18:01:00.001+01:00</published><updated>2011-11-30T10:20:54.650+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-30T10:20:54.650+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="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="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Apache Tapestry 5.3 publicado (Notas de publicación)</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;/div&gt;
La versión 5.3 de del framework &lt;a href="http://tapestry.apache.org/"&gt;Apache Tapestry&lt;/a&gt; para el desarrollo de aplicaciones web dinámicas en Java ha sido publicada y así &lt;a href="http://mail-archives.apache.org/mod_mbox/tapestry-users/201111.mbox/%3CCACd-vsNnhX-zTTfM=-KNZCDsMeOgmzOE7TqeOC5-F44wpB-Mwg@mail.gmail.com%3E"&gt;ha sido anunciada en la lista de correo&lt;/a&gt;. Continúa con la línea de las series 5.x e incorpora muchas novedades, mejoras y correcciones de errores, casi 350 peticiones han sido resueltas. Varias de estas nuevas características se suman a algunos&amp;nbsp;&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/motivos-para-elegir-apache-tapestry.html"&gt;Motivos para elegir el framework Apache Tapestry&lt;/a&gt;. Las lista de novedades es la siguiente:&lt;br /&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;br /&gt;
&lt;b&gt;Documentación&lt;/b&gt;&lt;br /&gt;
La ya buena documentación que existía ha sido mejorada y ampliada con nuevas secciones. Así tenemos&amp;nbsp;&lt;a href="http://tapestry.apache.org/tapestry-tutorial.html"&gt;un tutotial&lt;/a&gt;&amp;nbsp;con el que los usuarios que estén empezando les resulte más fácil la tarea, incluye las herramientas a utilizar, las dependencias, como emepezar una aplicación rápidamente utilizando el archetype de maven y un par de ejemplos sencillos para empezar a hacer algo asi como usar Tapestry con Hibernate. También se ha añadido&amp;nbsp;&lt;a href="http://tapestry.apache.org/cookbook.html"&gt;una sección de recetas&lt;/a&gt;&amp;nbsp;sobre como hacer cosas habituales en muchas aplicaciones. También se ha añadido una sección de preguntas frecuentes y unas páginas de consulta para características de uso habitual (&lt;a href="http://tapestry.apache.org/application-module-class-cheat-sheet.html"&gt;Application Module Class Cheat Sheet&lt;/a&gt;,&amp;nbsp;&lt;a href="http://tapestry.apache.org/component-cheat-sheet.html"&gt;Component Cheat Sheet&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Alertas&lt;/b&gt;&lt;br /&gt;
Tapestry ahora tiene un mecanismo central para manejar las alertas de usuario. Esto incluye el servicio &lt;a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/alerts/AlertManager.html"&gt;AlertManager&lt;/a&gt; y el componente &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Alerts.html"&gt;Alerts&lt;/a&gt;. Simplmente añade el componente Alerts a tu layout estandar y Tapestry se encarga del resto. Las alertas se pueden añadir tanto en las peticiones tradicionales como en las peticiones Ajax y pueden ser transitorias (se muestran durante unos segundos), normales o fijas (se mantienen entre peticiones hasta que el usuario las elimina expresamente haciendo clic). Las alertas pueden ser de tres severidades: info, advertencia (warn) y error. La apariencia puede ser personalizada sobreescribiendo las reglas CSS de Tapestry.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Salida de comentarios&lt;/b&gt;&lt;br /&gt;
Ahora es posible que Tapestry emita comentarios de salida. Esto son comentarios (como &amp;lt;!--BEGIN Index:loop (context:Index.tml, line 15)--&amp;gt;) que ayudan a depurar las etiquetas de salida en la parte del cliente. Esto se habilita para todas las peticiones que usen el símbolo de configuración «tapestry.component-render-tracing-enabled» y pueden ser añadidas a cualquier petición añadiendo el parámetro «t:component-trace=true» a la URL. Esto aumenta significativamente el tamaño de las etiquetas de salida pero puede ser muy útil con layouts compejos para determinar que componente es el responsable de cada porción de la página.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Contribuciones de servicio adaptables&lt;/b&gt;&lt;br /&gt;
Al hacer contribuciones a un servicio ahora no se está limitado a contribuir un valor que sea asignable al tipo asociado con la configuración. Objetos de cualquier tipo pueden ser contribuidos y el servicio TypeCoercer será usado para transformar el valor al tipo de la configuración (&lt;a href="http://tapestry.apache.org/typecoercer-service.html"&gt;Ver documentación TypeCoercer&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Mejoras en la depuración de componentes&lt;/b&gt;&lt;br /&gt;
Dada la forma en como Tapestry instrumentaba las páginas y componentes usar un depurador ha sido dificultoso con las clases de páginas y componentes, cualquier campo mutable muestra su valor por defecto en el depurador independientemente de que haya sido escrito en el campo o leído de él. En el modo desarrollo de Tapestry 5.3 los valores ocultos leen de y escriben a esos campos en los propios campos (esto también ha sido corregido en la rama de mantenimiento 5.2.5). Este ocultamiento no ocurre en el modo producción para evitar potenciales pérdidas de memorias.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Recarga deshabilitada en producción&lt;/b&gt;&lt;br /&gt;
Desde ahora no se buscan cambios en los archivos de clases, plantillas o catálogos de mensajes en el modo producción. Es asumido que las aplicaciones Tapestry son empaquetadas en archivos WAR en producción y que cambiar el archivo WAR causa que el contenedor de servlet redespliegue la aplicación entera. Este cambio mejora el rendimiento y reduce el consumo de memoria en las aplicaciones.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;JavaDoc Tapestry&lt;/b&gt;&lt;br /&gt;
Tapestry ahora incluye una nueva librería , tapestry-javadoc, que reemplaza el antiguo componente de informes basado en Maven. Simplemente poniendo una anotación @tapestrydoc en el &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/package-summary.html"&gt;JavaDoc de los componentes Tapestry&lt;/a&gt; generará la documentación completa como parte del JavaDoc... no mas cambios entre el JavaDoc y el informe del componente generado por Maven y no mas dependencia en Maven para la documentación de los componentes. Los estilos del &lt;a href="http://tapestry.apache.org/5.3/apidocs/"&gt;JavaDoc&lt;/a&gt; han sido adaptados a los colores del proyecto.&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/-H277tUo_jOw/Ts0qHsCKLBI/AAAAAAAABAs/IDm5LLTZ4nQ/s1600/tapestry-javadoc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-H277tUo_jOw/Ts0qHsCKLBI/AAAAAAAABAs/IDm5LLTZ4nQ/s200/tapestry-javadoc.png" width="114" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-Mn62jtTkAB4/Ts0qCaBq4vI/AAAAAAAABAk/04mAj47ofY0/s1600/component-tapestry-javadoc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-Mn62jtTkAB4/Ts0qCaBq4vI/AAAAAAAABAk/04mAj47ofY0/s200/component-tapestry-javadoc.png" width="65" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Template skinning&lt;/b&gt;&lt;br /&gt;
Tapestry añade la posibilidad de personalizar y/o tematizar («skin and/or theme») las páginas o componentes. Es una extensión de como Tapestry gestiona las páginas por locale pero añade ejes definidos por aplicación además de reglas para encontrar recursos. Ver &lt;a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/services/pageload/ComponentResourceSelector.html"&gt;ComponentResourceSelector&lt;/a&gt; y &lt;a href="http://tapestry.apache.org/5.3/apidocs/org/apache/tapestry5/services/pageload/ComponentRequestSelectorAnalyzer.html"&gt;ComponentRequestSelectorAnalyzer&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
También hay un nuevo componente &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Dynamic.html"&gt;Dynamic&lt;/a&gt; el cual usa una plantilla externa (no una plantilla de Tapestry) que puede ser seleccionada en tiempo de ejecución.&lt;br /&gt;
&lt;br /&gt;
Mediante esta funcionalidad ahora la plantilla que se aplica para una página o componentes no solo vendrá determinado por el locale de la aplicación sino que podremos introducir más variables en la elección. Una de ellas podría ser el tipo de cliente, como por ejemplo un dispositivo móvil del que hace la petición u otros criterios para asi mostrar contenido más adaptado o personalizado.&lt;br /&gt;
&lt;br /&gt;
En el siguiente enlace de puede ver una demostración de como aplicar la funcionalidad &lt;a href="http://blog.tapestry5.de/index.php/2011/06/24/template-skinning/"&gt;Template Skinning&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Evento de formulario cancelado&lt;/b&gt;&lt;br /&gt;
Los componentes de formulario ahora reconocen cuando el formulario de la parte cliente fue cancelado. Un nuevo evento «cancelled» es disparado al principio del proceso de envío lo cual permite a la página saltar todas las actualizaciones y validaciones en el servidor cuando sea lo deseado.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Restricciones implicitas en OrderedConfiguration&lt;/b&gt;&lt;br /&gt;
Al usar OrderedConfiguration.add() sin restricciones Tapestry ahora implicitamente ordenará el elmento añadido después del elemento añadido anteriormente dentro del mismo método. En versiones anteriores esos elementos eran añadidos sin restricciones. Esto hace más fácil contribuir un grupo de elementos relacionados con un orden implicito.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Nuevas validaciones de las clases de componente&lt;/b&gt;&lt;br /&gt;
Tapestry incluye nuevas validaciones de clases de componente para ayudar a reducir algunos errores comunes. Ahora comprueba que los componentes referenciados por un manejador de eventos concuerde con un componente definido en la plantilla... esto identifica rápidamente errores tipográficos en los nombres de métodos. Esto es, si la firma de un manejador de evento es «onFormularioRegistroSubmit()» se comprueba que exista en la plantilla un componente de id «FormularioRegistro». Esta comprobación puede ser deshabilitada con un símbolo de configuración de tal modo que aplicaciones 5.2 existentes que tengan este tipo de errores todavía puedan funcionar (esto es, tendrá manejadores de eventos muertos que nunca serán invocados).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Carpeta de aplicación&lt;/b&gt;&lt;br /&gt;
Tapestry ahora puede ser configurado para ejecutarse dentro de una carpeta, lo cual puede ser útil cuando se ejecuta dentro de una aplicación web que contiene otros servlets y filtros como una forma de prevenir conflictos. En vez de ejecutarse en la raiz del contexto de la aplicación.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Mejorado el arquetipo de inicio rápido&lt;/b&gt;&lt;br /&gt;
El arquetipo de Maven ha sido actualizado y ahora muestra varias nuevas funcionalidades de uso común. Además añade soporte para &lt;a href="http://www.gradle.org/"&gt;Gradle&lt;/a&gt; (&lt;a href="http://tapestryjava.blogspot.com/2011/10/tapestry-53-maven-archetype.html"&gt;Referencia&lt;/a&gt;). La forma de usarlo desde la linea de comandos es:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;$MAVEN_HOME/bin/mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org/&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Páginas blancas&lt;/b&gt;&lt;br /&gt;
La nueva anotación @WhitelistAccessOnly marca una página como accesible solo por una lista blanca de clientes. Las reglas para la lista son extensibles. La regla por defecto de la lista blanca es que sea accedida desde 127.0.0.1. Usa esta anotación en páginas que pueden exponer datos sensibles como paneles informativos incorporados.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Catálogo de página&lt;/b&gt;&lt;br /&gt;
Las aplicacicones Tapestry incluyen una página PageCatalog la cual lista todas las páginas cargadas de la aplicación con detalles acerca de su tiempo de construcción y número de componentes. La página requiere acceso en la lista blanca (ver en comentario anterior). Muestra alguna información que solo está disponible en el modo desarrollo. PageCatalog puede ser usada para cargar todas la páginas de la aplicación lo cual es útil para localizar problemas en las páginas... y es especialmente útil al aumentar de una versión anterior de Tapestry.&lt;br /&gt;
&lt;br /&gt;
Otra página de la que podemos obtener información acerca de los servicios de la aplicación es ServiceStatus donde podremos ver el estado de los mismos.&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/-_DOvl0lANlE/Ts0sPpUCfiI/AAAAAAAABA0/IfFcG-M289g/s1600/PageCatalog.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="166" src="http://2.bp.blogspot.com/-_DOvl0lANlE/Ts0sPpUCfiI/AAAAAAAABA0/IfFcG-M289g/s200/PageCatalog.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-PBU_7vStIxQ/Ts0sU-L_wxI/AAAAAAAABA8/48UDM6gxsm8/s1600/ServiceStatus.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-PBU_7vStIxQ/Ts0sU-L_wxI/AAAAAAAABA8/48UDM6gxsm8/s200/ServiceStatus.png" width="84" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;Logging en la parte ciente JavaScript&lt;/b&gt;&lt;br /&gt;
La consola en la parte cliente Blackbird ha sido eliminada. En su lugar están los mensajes de la consola flotante combinada con el logging a la consola de Firebug o WebKit. Tapestry no captura más las excepciones de JavaScript de inicialización de modo que pueden ser informadas apropiadamente en la consola nativa. Estos cambios deberían hacer la depuración de JavaScript en el lado cliente mucho más fácil.&lt;br /&gt;
&lt;br /&gt;
El informe de error es una de las cosas buenas de Tapestry. Con esta nueva versión esta característica se ha extendido a las peticiones Ajax con lo que cuando se produzca una excepción en el servidor se nos mostrará un informe en una ventana emergente con un contenido similar a las excepciones de las peticiones normales. En este &lt;a href="http://tapestryjava.blogspot.com/2011/08/tapestry-53-ajax-exception-reporting.html"&gt;screencast&lt;/a&gt; se puede ver en funcionamiento.&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/-g2lol_68KUc/Ts0vSnfVFRI/AAAAAAAABBs/dL3sVJKqovM/s1600/exception-report.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-g2lol_68KUc/Ts0vSnfVFRI/AAAAAAAABBs/dL3sVJKqovM/s200/exception-report.png" width="25" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Otra lista de novedades no mencionadas en la notas de lanzamiento oficial:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Nuevos componentes&lt;/b&gt;&lt;br /&gt;
Se han añadido unos cuantos nuevos componentes al los ya &lt;a href="http://tapestry.apache.org/component-reference.html"&gt;presentes en el core&lt;/a&gt;. Estos son:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/kaptcha/components/KaptchaImage.html"&gt;KaptchaImage&lt;/a&gt; / &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/kaptcha/components/KaptchaField.html"&gt;KaptchaField&lt;/a&gt;: Permite evitar el spam en los formularios.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Tree.html"&gt;Tree&lt;/a&gt;: componente ábol que pemite mostrar información jerárquica con nodos expandibles, contraibles y seleccionables. Es personalizable y basado en Ajax.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Checklist.html"&gt;Checklist&lt;/a&gt;: componente de selección multiple consistente en diferentes checkbox.&lt;/li&gt;
&lt;/ul&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;Nueva capa de abstracción JavaScript&lt;/b&gt;&lt;br /&gt;
En estos momentos Tapestry está atado a Prototype y Scriptaculous por compatibilidad con anteriores versiones (aunque se puede utilizar perfectamente junto con otros frameworks javascript como jQuery). En un futuro la nueva capa capa de abstracción permitirá usar cualquier librería JavaScript. La transición se completará en la versión 5.4.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Soporte Ajax mejorado&lt;/b&gt;&lt;br /&gt;
La clase MiltiZoneUpdate ha sido marcada como obsoleta en favor del servicio AjaxResponseRenderer. Esta nueva clase permite actualizar una zona con el contenido de Block, Component u otro objeto capaz de ser convertido a RenderCommand. Permite además importar una librería JavaScript como parte de la respuesta Ajax y añadir una llamada a una función en el lado del cliente, útil y a veces necesario para procesar la petición Ajax correctamente.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Integración con JSR 330 (Dependency Injection for Java)&lt;/b&gt;&lt;br /&gt;
Empezando desde Tapestry 5.3 es posible usar las anotaciones que define la especificación JSR-330 en la capa de servicios de tal modo que no dependan de Tapestry.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Integración JPA2&lt;/b&gt;&lt;br /&gt;
Integración nativa con JPA2 proporcionando compatibilidad hacia atrás en futuras versiones y posibilidad de usar múltiples unidades de persistencia en la misma aplicación. Permite configurar JPA sin necesidad de XML.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Herramienta construcción, Gradle&lt;/b&gt;&lt;br /&gt;
Tapestry ha pasado de utilizar Maven a usar Gradle como herramienta de contrucción y compilación para el propio proyecto. Gradle aporta nuevas funcionalidades, simplifica otras y evita algunos de los aspectos menos deseables de Maven.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Actualización Jumpstart&lt;/b&gt;&lt;br /&gt;
Actualizada la aplicación de referencia &lt;a href="http://draft.blogger.com/jumpstart.doublenegative.com.au/jumpstart/"&gt;Jumpstart&lt;/a&gt; a la versión 5.3 con numerosos ejemplos que muestran funcionalidades, y con la posibilidad de ver el código fuente de esos ejemplos.&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/-AbUNzcQIghE/Ts0tofI3_LI/AAAAAAAABBE/wAldUiiMq9A/s1600/JumpStart.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="182" src="http://3.bp.blogspot.com/-AbUNzcQIghE/Ts0tofI3_LI/AAAAAAAABBE/wAldUiiMq9A/s200/JumpStart.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Otras&lt;/b&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Incorporación de &lt;a href="http://documentcloud.github.com/underscore/"&gt;Underscore.js&lt;/a&gt; que añade programación funcional a JavaScript (en modo no conflicto).&lt;/li&gt;
&lt;li&gt;Coercion de String-a-Enum sin necesidad de hacer una contribución en el contenedor IOC.&lt;/li&gt;
&lt;li&gt;Compresión JavaScript y CSS a través de de la librería &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUICompressor library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Más rápido y más eficiente en uso de memoria.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-xCDcX0ielNc/Ts0uApTAeLI/AAAAAAAABBc/Y66Y5mg0qM0/s1600/no-whitespace-compress.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="108" src="http://3.bp.blogspot.com/-xCDcX0ielNc/Ts0uApTAeLI/AAAAAAAABBc/Y66Y5mg0qM0/s200/no-whitespace-compress.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-Wkn0Ui5-MJo/Ts0uB8JA0yI/AAAAAAAABBk/IX_hDZB89vI/s1600/whitespace-compress.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="108" src="http://3.bp.blogspot.com/-Wkn0Ui5-MJo/Ts0uB8JA0yI/AAAAAAAABBk/IX_hDZB89vI/s200/whitespace-compress.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://2.bp.blogspot.com/-p1wALjPx-gI/Ts0t9-FbqvI/AAAAAAAABBU/PV8F85Vb22I/s1600/no-css-minification.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="108" src="http://2.bp.blogspot.com/-p1wALjPx-gI/Ts0t9-FbqvI/AAAAAAAABBU/PV8F85Vb22I/s200/no-css-minification.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-pP8rK8cF7fE/Ts0t85TtwWI/AAAAAAAABBM/cXRdw_XWX8w/s1600/css-minification.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="108" src="http://1.bp.blogspot.com/-pP8rK8cF7fE/Ts0t85TtwWI/AAAAAAAABBM/cXRdw_XWX8w/s200/css-minification.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Cambios disruptivos&lt;/b&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Tapestry depende ahora del Servlet API versión 2.5 con lo que la versión mínima para ejecutarlo ahora será Tomcat 6.0+.&lt;/li&gt;
&lt;li&gt;Actualizado Prototype a la versión 1.7.&lt;/li&gt;
&lt;li&gt;Las plantillas sin  son tratadas como si tuvieran el doctype de HTML5 () pudiendose usar entidades como © o &amp;amp;nbsp; sin ver errores de parseo de anteriores versiones.&lt;/li&gt;
&lt;/ul&gt;
Algunas interfaces y APIS en Tapstry 5.3 serán eliminadas en Tapestry 5.4 o posteriores. Estas incluyen:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Entorno RenderSupport (reemplazada con el entorno JavaScriptSupport).&lt;/li&gt;
&lt;li&gt;El objeto MultiZoneUpdate, reemplazada con AjaxResponseRenderer.&lt;/li&gt;
&lt;li&gt;El servicio ClassFactory y la interfaz ClassFab (reemplazada con el servicio PlasticProxyFactory y la interfaz PlasticClass).&lt;/li&gt;
&lt;li&gt;La funcionalidad "suppress redirects", que permitía a los eventos de los componentes responder directamente con HTML, como en Tapestry 4.&lt;/li&gt;
&lt;/ul&gt;
Salvo que se usen &lt;a href="https://issues.apache.org/jira/browse/TAP5-1432"&gt;características marcadas como obsoletas en Tapestry 5.2&lt;/a&gt; en la nueva versión la actualización no requerirá más que &lt;a href="http://tapestry.apache.org/2011/10/31/tapestry-53-release-candidate.html"&gt;actualizar a las nuevas dependencias&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Y una lista larga de corrección de errores y mejoras. La lista completa está en las &lt;a href="http://tapestry.apache.org/release-notes-53.html"&gt;notas de publicación de tapestry 5.3 oficiales&lt;/a&gt;&amp;nbsp;y aquí &lt;a href="http://blog.tapestry5.de/index.php/2011/08/22/tapestry-5-3-preview/"&gt;una lista de novedades en inglés&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/index.html"&gt;http://tapestry.apache.org/index.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/download.html"&gt;http://tapestry.apache.org/download.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/motivos-para-elegir-apache-tapestry.html"&gt;Motivos para elegir el framework Apache Tapestry&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-3446299687808840618?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/8j6ifK4W-zo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/3446299687808840618/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/11/apache-tapestry-53-publicado.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3446299687808840618?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3446299687808840618?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/8j6ifK4W-zo/apache-tapestry-53-publicado.html" title="Apache Tapestry 5.3 publicado (Notas de publicación)" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/11/apache-tapestry-53-publicado.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UDRXc9eCp7ImA9WhRSFkU.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7299577762955959227</id><published>2011-11-18T20:24:00.001+01:00</published><updated>2011-11-19T06:41:14.960+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-19T06:41:14.960+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="wallpaper" /><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="gnome" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Personalizar GNOME (iconos, temas, extensiones, opciones)</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&gt;
&lt;img border="0" height="76" src="http://1.bp.blogspot.com/-h-QR-vlTp1s/TZ8-U-5DhII/AAAAAAAAAn0/421PkrTee50/s200/gnome.png" width="200" alt="GNOME" title="GNOME" /&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&gt;
&lt;img border="0" height="65" src="http://3.bp.blogspot.com/-XVsmee9V9Vo/S_LjDl4iWZI/AAAAAAAAAQI/LcBAWOoav-s/s200/archlinux-official-dark.png" width="200" alt="Arch Linux" title="Arch Linux"/&gt;&lt;/div&gt;
Una vez que tenemos instalado nuestro sistema una de las primeras cosa que solemos hacer es personalizarlo cambiando el fondo de pantalla, instalando un pack de iconos o cambiando el tema de las ventanas. Aunque GNOME con su versión 3.0 o 3.2 ha perdido algunas funcionalidades y algunas posibilidadesd de configuración la herramienta &lt;a href="http://www.archlinux.org/packages/extra/any/gnome-tweak-tool/"&gt;gnome-tweak-tool&lt;/a&gt; permite configurar las opciones más habituales que querrán la mayoría de los usuarios. Para instalarla en arch linux:
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;# pacman -S gnome-tweak-tool&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Una vez instalada veremos su icono en el menu Aplicaciones &amp;gt; Accesorios &amp;gt; Configuración avanzada.&lt;br /&gt;
&lt;br /&gt;
En la pestaña &lt;b&gt;Escritorio&lt;/b&gt; podemos configurar algunos comportamientos como mostrar la visibilidad de los iconos Computadora, Papelera, Carpeta Home.&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/-cfzTF2nVgVs/Tsaw33K2sUI/AAAAAAAAA_U/_LBUB2cJ0Qo/s1600/gnome-tweak-tool-1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://2.bp.blogspot.com/-cfzTF2nVgVs/Tsaw33K2sUI/AAAAAAAAA_U/_LBUB2cJ0Qo/s200/gnome-tweak-tool-1.jpg" 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;
En &lt;b&gt;Extensiones de GNOME-Shell&lt;/b&gt; podemos instalar nuevas extensiones que descarguemos aunque es mejor opción hacerlo con el gestor de paquetes de nuestra distribución. Hay una &lt;a href="http://www.archlinux.org/packages/?sort=&amp;amp;q=gnome-shell-extension&amp;amp;maintainer=&amp;amp;last_update=&amp;amp;flagged=&amp;amp;limit=50"&gt;lista de extensiones disponibles para Arch linux&lt;/a&gt; que pueden instalarse individualmente y que modifican el comportamiento por defecto del shell de gnome.&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/-eC8drFZzrXs/Tsaw4hr9KnI/AAAAAAAAA_c/nsTdlIyi-ms/s1600/gnome-tweak-tool-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://2.bp.blogspot.com/-eC8drFZzrXs/Tsaw4hr9KnI/AAAAAAAAA_c/nsTdlIyi-ms/s200/gnome-tweak-tool-2.jpg" 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;
En &lt;b&gt;GNOME-Shell&lt;/b&gt; podemos personalizar la información que se muestra en el reloj, las acciones a realizar al cerrar la tapa del portátil y los botones que queremos en las ventanas.&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/-WrfsBOoJi88/Tsaw50xlZVI/AAAAAAAAA_k/JHSFJe8eKY0/s1600/gnome-tweak-tool-3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://3.bp.blogspot.com/-WrfsBOoJi88/Tsaw50xlZVI/AAAAAAAAA_k/JHSFJe8eKY0/s200/gnome-tweak-tool-3.jpg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
En &lt;b&gt;Tema&lt;/b&gt; podemos configurar los temas del puntero del ratón, iconos, tema de los componentes GTK+ y de las ventanas. Uno de los temas de iconos más conocidos es el tema faenza disponible en Arch linux en el repositorio AUR con el nombre de &lt;a href="https://aur.archlinux.org/packages.php?ID=39334"&gt;paquete faenza-icon-theme&lt;/a&gt;. Aunque &lt;a href="https://aur.archlinux.org/packages.php?O=0&amp;amp;K=icon-theme&amp;amp;do_Search=Go"&gt;temas de iconos hay muchos&lt;/a&gt;.&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/-Foffyn1OxoA/Tsaw63od-9I/AAAAAAAAA_s/E1K96_KafHs/s1600/gnome-tweak-tool-4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://3.bp.blogspot.com/-Foffyn1OxoA/Tsaw63od-9I/AAAAAAAAA_s/E1K96_KafHs/s200/gnome-tweak-tool-4.jpg" 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;
En &lt;b&gt;Tipografías&lt;/b&gt; se pueden cambiar las fuentes y tamaños de la fuente usada en el sistema, de los documentos, en el título de la ventana y alguna otra opción.&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/-J55ANtwA5pw/Tsaw7iAJjUI/AAAAAAAAA_0/V3wUBT_HoUY/s1600/gnome-tweak-tool-5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://2.bp.blogspot.com/-J55ANtwA5pw/Tsaw7iAJjUI/AAAAAAAAA_0/V3wUBT_HoUY/s200/gnome-tweak-tool-5.jpg" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-EQbSuZKopRQ/Tsaw9E6-TDI/AAAAAAAAA_8/XdaJsewRSx4/s1600/gnome-tweak-tool-5-1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="161" src="http://2.bp.blogspot.com/-EQbSuZKopRQ/Tsaw9E6-TDI/AAAAAAAAA_8/XdaJsewRSx4/s200/gnome-tweak-tool-5-1.jpg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
En la opción &lt;b&gt;Ventanas&lt;/b&gt; se pueden modificar algunos comportamientos al hacer clic en las ventanas.&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/-1pJXuymCz4g/Tsaw9521rxI/AAAAAAAABAE/tZyVS0wgXLc/s1600/gnome-tweak-tool-6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://4.bp.blogspot.com/-1pJXuymCz4g/Tsaw9521rxI/AAAAAAAABAE/tZyVS0wgXLc/s200/gnome-tweak-tool-6.jpg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
La falta de configuración de la versión 3 de gnome es una de las críticas que se le ha hecho pero a medida que vaya evolucionando en versiones lo hará al mismo tiempo en funcionalidades y opciones de configuración, es cuestión de tiempo.&lt;br /&gt;
&lt;br /&gt;
Yo no soy de tener una personalización extrema del escritorio prácticamente tengo con la configuración por defecto exceptuando el &lt;a href="https://aur.archlinux.org/packages.php?ID=39334"&gt;tema de iconos faenza&lt;/a&gt;.&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/-I9Pm01LevoA/TsaxusX47lI/AAAAAAAABAM/6k6Mdh8XCXs/s1600/gnome32-1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="112" src="http://3.bp.blogspot.com/-I9Pm01LevoA/TsaxusX47lI/AAAAAAAABAM/6k6Mdh8XCXs/s200/gnome32-1.jpg" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-ysd4dJPtARQ/TsaxwLEIr7I/AAAAAAAABAU/4K8PTCKu5J4/s1600/gnome32-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="112" src="http://3.bp.blogspot.com/-ysd4dJPtARQ/TsaxwLEIr7I/AAAAAAAABAU/4K8PTCKu5J4/s200/gnome32-2.jpg" 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/-rYaXEFwI3zg/TsaxyJOnZZI/AAAAAAAABAc/mUPTkzL2tNM/s1600/gnome32-3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="112" src="http://3.bp.blogspot.com/-rYaXEFwI3zg/TsaxyJOnZZI/AAAAAAAABAc/mUPTkzL2tNM/s200/gnome32-3.jpg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-7299577762955959227?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/cOg3i4cuxtE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7299577762955959227/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/11/personalizar-gnome-iconos-temas.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7299577762955959227?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7299577762955959227?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/cOg3i4cuxtE/personalizar-gnome-iconos-temas.html" title="Personalizar GNOME (iconos, temas, extensiones, opciones)" /><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/-h-QR-vlTp1s/TZ8-U-5DhII/AAAAAAAAAn0/421PkrTee50/s72-c/gnome.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/11/personalizar-gnome-iconos-temas.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak4BRnc-fip7ImA9WhRSEk0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7322503047233098590</id><published>2011-11-11T20:52:00.001+01:00</published><updated>2011-11-13T19:29:17.956+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-13T19:29:17.956+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Internacionalización (i18n) de campos con Hibernate</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;/div&gt;
Si estamos desarrollando un sitio web que soporta varios idiomas necesitaremos internacionalizar (i18n) los literales que aparecen en él. Esto incluye también tenerlo en cuenta y solucionarlo en los nombres, descripciones y textos que guardamos en la base de datos de las entidades de dominio y que puedan aparecer en en el html generado.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&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;
La primera solución que se nos ocurre para las entidades de dominio es crear un campo por cada idioma y concepto a internacionalizar. Sin embargo, esto tiene el problema de que en la base de datos el número de campos crecerá rápidamente y si tenemos muchos elementos a internacionalizar y muchos idiomas el número de campos puede ser un problema por el tamaño de fila, y aunque los &lt;a href="http://www.postgresql.org/about/"&gt;limites en posgres&lt;/a&gt; y los &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/column-count-limit.html"&gt;límites de mysql&lt;/a&gt; son elevados y más que suficientes para la mayoría de los casos, con la solución anterior podemos llegar a ellos o estar peligrosamente cerca.&lt;br /&gt;
&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&gt;&lt;a href="http://4.bp.blogspot.com/-MFcqncf0Nh8/Tr6R8ZWhFTI/AAAAAAAAA_M/sNshk77s9CA/s1600/hibernate-schema-tradicional.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-MFcqncf0Nh8/Tr6R8ZWhFTI/AAAAAAAAA_M/sNshk77s9CA/s200/hibernate-schema-tradicional.png" width="111" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="font-size: 13px;"&gt;Diagrama entidad relación de base de datos&lt;br /&gt;
Primera solución (variación número columnas)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Para evitarnos problemas las buenas prácticas de diseño de las bases de datos dicen que las &lt;a href="http://es.wikipedia.org/wiki/Normalizaci%C3%B3n_de_bases_de_datos"&gt;tablas han de estar normalizadas&lt;/a&gt; y siguiengo la solución anterior tendríamos variaciones en el número de columnas con lo que inclumpliríamos la primera forma normal (1FN). ¿Pero porque inclumplir esta forma normal puede ser un problema? Porque en el momento que tengamos que soportar un nuevo idioma deberemos modificar el esquema de la base de datos añadiendo un campo por cada concepto de las entidades que haya que internacionalizar. Y hacer esto en una base de datos que está en producción puede ser una fuente de problemas y un peligro. Además, en tablas con muchos registros (de unos cuantos miles o cientos de miles) añadir una columna puede ser muy lento pudiendo llegar a horas o más tiempo cosa que dependiendo del proyecto no es viable al poder tener que realizar paradas largas por mantenimiento.&lt;br /&gt;
&lt;br /&gt;
La solución que voy a explicar a continuación es crear una tabla Diccionario la cual relacionaremos con las tablas de los datos de dominio (Producto) y una tabla Traduccion que relacionaremos con la tabla Diccionario y que contendrá las traducciones para cada idioma. La tabla Traduccion contendrá tres campos la clave primaria del diccionario, el idioma de la traduccion y el literal de la traducción propiamente dicho con lo que ya no tendremos variaciones en el número columnas. En la tabla Traduccion la clave primaria estará formada por la clave del diccionario y el campo locale. El esquema que tendríamos sería el siguiente:&lt;br /&gt;
&lt;div style="text-align: -webkit-auto;"&gt;
&lt;br /&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/-KET3xsxmbcM/Tr19ssItyEI/AAAAAAAAA-8/xjNi5F6D0Q4/s1600/hibernate-schema.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="81" src="http://3.bp.blogspot.com/-KET3xsxmbcM/Tr19ssItyEI/AAAAAAAAA-8/xjNi5F6D0Q4/s320/hibernate-schema.png" 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;Diagrama entidad relación de base de datos.&lt;br /&gt;
Producto-Diccionario 1:1&lt;br /&gt;
Diccionario-Traduccion 1:N&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&amp;nbsp;A primera vista la tabla Diccionario no tiene mucho sentido pero nos permitirá crear en &lt;a href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; una clase donde podremos incluir algunos métodos de utilidad de forma que podamos acceder a las traducciones más cómodamente. Para modelar este esquema con hibernate necesitaremos las siguientes entidades de dominio, la entidad Producto, Diccionario, Traduccion y TraduccionPK (necesaria en Hibernate al tener Traduccion una clave compuesta por dos campos o columnas).&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;// Producto.java (Código resumido)
...

@Entity
@Table(name = "Producto")
public class Producto implements Serializable {
 ...

 &lt;b&gt;@OneToOne(cascade = CascadeType.ALL)
 @JoinColumn(name = "nombre_id", nullable = true)
 private Diccionario nombre;

 @OneToOne(cascade = CascadeType.ALL)
 @JoinColumn(name = "descripcion_id", nullable = true)
 private Diccionario descripcion;&lt;/b&gt;

 ...
}

// Diccionario.java
package com.blogspot.elblogdepicodev.domain;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.apache.commons.lang3.StringUtils;

import com.blogspot.elblogdepicodev.misc.AppThreadLocal;

@Entity
@Table(name = "diccionario")
public class Diccionario implements Serializable {

 private static final long serialVersionUID = -1210174827995726573L;
 
 private static final int LONGITUD_TEXTO_ABREVIADO = 10;
 
 @Id
 @GeneratedValue
 private Long id;

 &lt;b&gt;@OneToMany(cascade = CascadeType.ALL)
 @JoinColumn(name = "diccionario_id")
 @MapKeyColumn(name = "locale")
 private Map&amp;lt;String, Traduccion&amp;gt; traducciones;&lt;/b&gt;
 
 public Diccionario() {
 }
 
 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public Map&amp;lt;String, Traduccion&amp;gt; getTraducciones() {
  if (traducciones == null) {
   traducciones = new HashMap&amp;lt;String, Traduccion&amp;gt;();
  }
  return traducciones;
 }

 public void setTraducciones(Map&amp;lt;String, Traduccion&amp;gt; textos) {
  this.traducciones = textos;
 }

 //
 public String getTexto() {
  return getTexto(getLocale());
 }

 public void setTexto(String texto) {
  setTexto(getLocale(), texto);
 }
 
 public String getTexto(Locale locale) {
  Traduccion t = getTraducciones().get(locale.toString());
  if (t == null) {
   return null;
  }
  return t.getTexto();
 }

 public void setTexto(Locale locale, String texto) {
  Traduccion t = getTraducciones().get(locale);
  if (t == null) {
   t = new Traduccion(this, getLocale().toString());
  }
  t.setTexto(texto);
 }

 //
 public boolean equals(Diccionario d) {
  if (d == null) {
   return false;
  }
  return getId().equals(d.getId());
 }

 @Override
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public String toString() {
  Map m = new HashMap();
  m.put("id", getId());
  m.put("texto", StringUtils.abbreviate(getTexto(), LONGITUD_TEXTO_ABREVIADO));
  return m.toString();
 }
    
 private Locale getLocale() {
  return AppThreadLocal.getPreferencias().getLocale();
 }
}

// Traduccion.java
package com.blogspot.elblogdepicodev.domain;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang3.StringUtils;

import com.blogspot.elblogdepicodev.misc.Utilidades;

@Entity
@Table(name = "traduccion")
public class Traduccion implements Serializable {

 private static final long serialVersionUID = -1210174827995726573L;
 
 private static final int LONGITUD_TEXTO_ABREVIADO = 10;

 &lt;b&gt;@Id
 private TraduccionPK id;&lt;/b&gt;

 @Column(length = 65536)
 private String texto;

 public Traduccion() {  
 }
 
 public Traduccion(Diccionario diccionario, String locale) {
  this.id = new TraduccionPK(diccionario, locale);
 }
 
 public TraduccionPK getId() {
  return id;
 }

 public void setId(TraduccionPK id) {
  this.id = id;
 }

 public String getTexto() {
  return texto;
 }

 public void setTexto(String texto) {
  this.texto = texto;
 }
 
 //
 public Locale getLocale() {
  return Utilidades.getLocale(id.getLocale());
 }

 //
 public boolean equals(Traduccion t) {
  if (t == null) {
   return false;
  }
  return getId().equals(t.getId());
 }

 @Override
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public String toString() {
  Map m = new HashMap();
  m.put("id", getId());
  m.put("locale", getId().getLocale());
  m.put("texto", StringUtils.abbreviate(getTexto(), LONGITUD_TEXTO_ABREVIADO));
  return m.toString();
 }
}

// TraduccionPK.java
package com.blogspot.elblogdepicodev.domain;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Basic;
import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;

@Embeddable
public class TraduccionPK implements Serializable {
 
 private static final long serialVersionUID = 4445136951104395835L;

 @ManyToOne
 private Diccionario diccionario;

 @Basic
 private String locale;

 public TraduccionPK() {  
 }
 
 public TraduccionPK(Diccionario diccionario, String locale) {
  this.diccionario = diccionario;
  this.locale = locale;
 }
 
 public Diccionario getDiccionario() {
  return diccionario;
 }

 public void setDiccionario(Diccionario diccionario) {
  this.diccionario = diccionario;
 }

 public String getLocale() {
  return locale;
 }

 public void setLocale(String locale) {
  this.locale = locale;
 }

 //
 public boolean equals(TraduccionPK t) {
  if (t == null) {
   return false;
  }
  return diccionario.equals(t.getDiccionario()) &amp;amp;&amp;amp; getLocale().equals(t.getLocale());
 }

 @Override
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public String toString() {
  Map m = new HashMap();
  m.put("dicionario", getDiccionario().toString());
  m.put("locale", getLocale());
  return m.toString();
 }
}

// Utilidades.java
public class Utilidades {
 ...

 public static Locale getLocale(String locale) {
  String[] s = locale.split("_");
  switch (s.length) {
  case 1: {
   return new Locale(s[0]);
  }
  case 2: {
   return new Locale(s[0], s[1]);
  }
  case 3: {
   return new Locale(s[0], s[1], s[2]);
  }
  default: {
   throw new IllegalArgumentException();
  }
  } 
 }

 ...
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Esta solución carga las traducciones para todos los idiomas de un diccionario cuando posiblemente solo necesitemos la traducción de un idioma en concreto, tal vez si no quisiésemos que se carguen todos los textos de las traducciones podríamos sacar el campo texto a otra tabla y relacionarla con la Traduccion con un id con lo que los campos de tablas nos quedarían: Traduccion (diccionario_id, locale, texto_id), Texto (id, texto). Aunque con esta última solución necesitamos lanzar una sql más por cada texto que queramos acceder.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://www.hibernate.org/"&gt;http://www.hibernate.org/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.postgresql.org/about/"&gt;http://www.postgresql.org/about/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://dev.mysql.com/doc/refman/5.0/en/column-count-limit.html"&gt;http://dev.mysql.com/doc/refman/5.0/en/column-count-limit.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://es.wikipedia.org/wiki/Normalizaci%C3%B3n_de_bases_de_datos"&gt;http://es.wikipedia.org/wiki/Normalizaci%C3%B3n_de_bases_de_datos&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://support.microsoft.com/kb/283878/es"&gt;http://support.microsoft.com/kb/283878/es&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-7322503047233098590?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/sXoH1dgh2Nw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7322503047233098590/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/11/internacionalizacion-i18n-de-campos-con.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7322503047233098590?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7322503047233098590?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/sXoH1dgh2Nw/internacionalizacion-i18n-de-campos-con.html" title="Internacionalización (i18n) de campos con Hibernate" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/11/internacionalizacion-i18n-de-campos-con.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYDSHg6fCp7ImA9WhRTGEo.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-8710921651251704121</id><published>2011-11-04T20:36:00.000+01:00</published><updated>2011-11-09T23:19:39.614+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-09T23:19:39.614+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Formatear precios con símbolo de moneda en Java</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;/div&gt;
Si estamos desarrollando una aplicación que muestre precios y trabaje con diferentes monedas o tengamos que mostrar el precio junto con la moneda de ese precio necesitaremos un NumberFormat configurado para que nos muestre además del precio el símbolo de la moneda. Esto nos será de mucha utilidad en aplicaciones de comercio electrónico, por ejemplo, ya que evitaremos hacer concatenaciones de cadenas String y el código será más legible y fácil de mantener.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;Locale locale = ...;
String simbolo = ...;

DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale);
dfs.setCurrencySymbol(simbolo);
String pattern = (Constantes.DIVISAS_SIMBOLO_FINAL.indexOf(simbolo) != -1) ? "#,##0.## ¤" : "¤ #,##0.##";
DecimalFormat df = new DecimalFormat(pattern, dfs);&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
El truco está en hacer uso de un &lt;a href="http://draft.blogger.com/blogger.g?blogID=2347950092468997900"&gt;DecimalFormatSymbols&lt;/a&gt; y un «¤» en el patrón de formateo. Otra cosa que también tendremos que tener en cuenta si para la moneda que vamos a mostrar el precio el símbolo se pone antes o después de él según la convención de esa moneda. Una vez tenemos el formateador de precio con el símbolo lo usamos de la siguiente forma:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;float importe = 13.19;
df.format(importe);&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Y obtendremos «13,19 €».&lt;br /&gt;
&lt;br /&gt;
La lógica de obtener el &lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/DecimalFormat.html"&gt;DecimalFormat&lt;/a&gt; estaría muy bien tenerlo en una clase de utilidad que lo construya para poder usarlo de diferentes sitios evitando duplicar código.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-8710921651251704121?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/G98YtemIl90" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/8710921651251704121/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/11/formatear-precios-con-simbolo-de-moneda.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8710921651251704121?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8710921651251704121?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/G98YtemIl90/formatear-precios-con-simbolo-de-moneda.html" title="Formatear precios con símbolo de moneda en Java" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/11/formatear-precios-con-simbolo-de-moneda.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0YDQ34yfyp7ImA9WhdaGE8.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-6469293592923059539</id><published>2011-10-28T20:06:00.000+02:00</published><updated>2011-10-28T20:06:12.097+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-28T20:06:12.097+02:00</app:edited><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="hola" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>¡Hola Planeta Código!</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&gt;
&lt;img alt="Planeta Código" border="0" height="65" src="http://1.bp.blogspot.com/-PfdTL9bYtAI/Tqrt9xOhJSI/AAAAAAAAA-0/UpJfjQvnePU/s320/planeta-codigo.jpg" title="Planeta Código" width="320" /&gt;&lt;/div&gt;
Últimamente las entradas que estoy escribiendo en el blog están más orientadas a la programación entorno a la plataforma Java que a GNU/Linux aunque siempre relacionadas con el software libre o código abierto. Por ello he incluido mi blog en el agregador de &lt;a href="http://www.planetacodigo.com/"&gt;Planeta Código&lt;/a&gt; más orientado a temas de programación.&lt;br /&gt;
&lt;br /&gt;
Al igual que cuando incluí mi blog en &lt;a href="http://www.planetalinux.org/es"&gt;Planeta Linux&lt;/a&gt;&amp;nbsp;el objetivo es llegar a más lectores y si es posible recibir más visitas y comentarios en el blog. Para los que no conozcan mi blog podeís visitarlo en &lt;a href="http://elblogdepicodev.blogspot.com/"&gt;http://elblogdepicodev.blogspot.com&lt;/a&gt;. La temática del mismo es el software libre, linux, la programación y cualquier cosa que tenga que ver con la tecnología o la informática.&lt;br /&gt;
&lt;br /&gt;
Algunas de las últimas entradas que he escrito en él son:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/motivos-para-elegir-apache-tapestry.html"&gt;Motivos para elegir el framework Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/10/peticiones-ajax-en-tapestry-ii.html"&gt;Peticiones Ajax en Tapestry (II)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/peticiones-ajax-en-tapestry.html"&gt;Peticiones Ajax en Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/seleccionar-el-lenguaje-locale-segun-el.html"&gt;Seleccionar el lenguaje (locale) según el dominio en Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/07/usar-apache-tapestry-5-con-groovy-u.html"&gt;Usar Apache Tapestry 5 con Groovy (u otros lenguajes de la JVM)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/componente-ajaxspinner-para-apache.html"&gt;Componente AjaxSpinner para Tapestry 5&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/10/ejemplo-sencillo-con-javacc-de-un.html"&gt;Ejemplo sencillo con JavaCC de un analizador léxico y sintáctico&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/10/configuracion-de-entorno-en-java-con.html"&gt;Configuración de entorno en Java con ayuda de Groovy&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/comparar-cadenas-en-java-ignorando.html"&gt;Comparar cadenas en Java ignorando acentos&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/integracion-continua-con-jenkins.html"&gt;Integración continua con Jenkins&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/08/internacionalizacion-lenguajes-con.html"&gt;Internacionalización a lenguajes con diferentes formas plurales en Java&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/06/hola-mundo-con-symfony-2-y-cherokee.html"&gt;Hola mundo con Symfony 2 y Cherokee&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/06/elegir-herramientas-para-un-proyecto.html"&gt;Elegir herramientas para un proyecto Java&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Espero que a alguno os resulten interesantes, si es así estáis invitados a suscribiros al &lt;a href="http://feeds.feedburner.com/blogspot/HkNj"&gt;feed de mi blog&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-6469293592923059539?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/YK_vjp5YfAg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/6469293592923059539/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/10/hola-planeta-codigo.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/6469293592923059539?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/6469293592923059539?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/YK_vjp5YfAg/hola-planeta-codigo.html" title="¡Hola Planeta Código!" /><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/-PfdTL9bYtAI/Tqrt9xOhJSI/AAAAAAAAA-0/UpJfjQvnePU/s72-c/planeta-codigo.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/10/hola-planeta-codigo.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYERXw7eCp7ImA9WhRTFE4.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-9077954053526241932</id><published>2011-10-21T19:59:00.000+02:00</published><updated>2011-11-04T20:48:24.200+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-04T20:48:24.200+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="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Peticiones Ajax en Tapestry (II)</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: right;"&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;
Como comentaba en la entrada &lt;a href="http://elblogdepicodev.blogspot.com/2011/09/peticiones-ajax-en-tapestry.html"&gt;Peticiones Ajax en Tapestry&lt;/a&gt; para hacerlas no es necesaria ni una sola linea de javascript. En la entrada Peticiones Ajax en Tapestry expliqué como hacer una petición Ajax que actualizaba un fragmento de una página y como hacer un petición Ajax que devolviese datos en Json para ser procesados en el cliente.&lt;br /&gt;
&lt;br /&gt;
Pero el soporte para las peticiones Ajax de Tapestry no se queda en esos dos casos. Otro escenario habitual en las páginas es la necesidad de actualizar más de una región de una misma página con una única petición. Para ello Tapestry proporciona la clase MultiZoneUpdate (en la versión 5.3 será desaconsejado su uso en favor del uso de AjaxResponseRenderer) que será el objeto que deberemos devolver en el método del evento que atiende la petición. Este objeto contendrá las referencias de las zonas que queremos actualzar en la página y los nuevos contenidos a mostrar en ellas, Tapestry se encargará en el cliente de actualizarlas automaticamente por nosotros sin que tengamos que hacer nada más.&lt;br /&gt;
&lt;br /&gt;
Veámoslo con un ejemplo.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;&amp;lt;t:zone t:id="usuarioZone" update="show"&amp;gt;
    &amp;lt;t:if test="service.usuario"&amp;gt;
        &amp;lt;span&amp;gt;${service.usuario.login} (&amp;lt;a t:id="cerrarSesion" t:type="actionlink"&amp;gt;Cerrar sesion&amp;lt;/a&amp;gt;)&amp;lt;/span&amp;gt;
        &amp;lt;p:else&amp;gt;&amp;lt;span&amp;gt;Invitado (&amp;lt;a t:type="pagelink" page="index"&amp;gt;Iniciar sesión/Registrarse&amp;lt;/a&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/p:else&amp;gt;
 &amp;lt;/t:if&amp;gt;
&amp;lt;/t:zone&amp;gt;
...
&amp;lt;t:zone t:id="formularioZone" update="show"&amp;gt;
 &amp;lt;t:if test="!service.usuario"&amp;gt;
  &amp;lt;t:form t:id="usuarioRegistradoForm" zone="datosZone"&amp;gt;
   &amp;lt;t:html5textfield type="email" value="urForm.usuario" validate="required" placeholder="Usuario"/&amp;gt;
   &amp;lt;t:html5textfield type="password" value="urForm.contrasena" validate="required" placeholder="Contraseña"/&amp;gt;&amp;lt;br/&amp;gt;    
   &amp;lt;t:submit t:id="usuarioRegistradoSubmit" value="Iniciar sesión" class="btn"/&amp;gt;
  &amp;lt;/t:form&amp;gt;
 &amp;lt;/t:if&amp;gt;

 &amp;lt;t:if test="service.usuario"&amp;gt;
  &amp;lt;t:form t:id="cerrarSesionUsuarioRegistradoForm"&amp;gt;
   ${service.usuario.login}&amp;lt;br/&amp;gt;
   &amp;lt;t:submit t:id="cerrarSesionUsuarioRegistradoSubmit" value="Cambiar de usuario" class="btn"/&amp;gt;
  &amp;lt;/t:form&amp;gt;
 &amp;lt;/t:if&amp;gt;
&amp;lt;/t:zone&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;Object onSubmitFromUsuarioRegistradoForm() throws IOException {
// Registrar al usuario (service.getUsuario() devolverá el objeto que representa al usuario)
...
 if (!service.getRequestGlobals().getRequest().isXHR()) {
  return null;
 }

 MultiZoneUpdate mzu = new MultiZoneUpdate("formularioZone", formularioZone.getBody()).add("usuarioZone", sesionZone.getBody());;

 return mzu;
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
En el ejemplo hay definidas dos zonas (usuarioZone, formularioZone) que son los componentes definidos con &amp;lt;t:zone&amp;gt;&lt;t:zone&gt;. Las zonas representan fragmentos de página cuyo contenido puede ser actualizado por una petición Ajax. Cuando se envía el formuario formularioZone se llama al método onSubmitFromUsuarioRegistradoForm en el cual comprobamos si la petición es una petición Ajax (service.getRequestGlobals().getRequest().isXHR()) si no lo es se actualizará toda la página pero todo seguirá funcionando, si lo es se devuelve un objeto MultiZoneUpdate y se actualizarán las zonas formularioZone y usuarioZone con el contenido de los propios componentes formularioZone y sesionZone aunque podríamos actualizarlas con el contenidos de otros componentes. Los eventos lanzados por Tapestry siguen por defecto una convención que es on[Evento]From[Componente] aunque podrímos darle el nombre que queramos si utilizamos anotaciones.&lt;/t:zone&gt;&lt;br /&gt;
&lt;br /&gt;
En Tapestry pasar de una aplicación no Ajax a una Ajax es muy sencillo basta insertar las zonas y en los métodos de los eventos devolver las zonas y contenidos con las que las queremos actualizar. Otro punto importante a destacar y que contribuye a facilitar la adición del soporte Ajax es que el contenido con el que se actualizan las zonas no lo tenemos que saparar en archivos individuales ni supondrá una reestructuración drástica en los archivos del proyecto como tendríamos que hacer en otros frameworks. También a destacar es que esta funcionalidad de actualizar dos fragmentos o zonas de una página no está presente en otros frameworks de por sí donde normalmente cada petición Ajax actualiza un único fragmento. El soporte en Tapestry para trabajar con Ajax es ¡sencillo y excelente!, de lo mejor que hay.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Nota:&lt;/b&gt; A partir de la versión 5.3 la clase MultiZoneUpdate ha sido marcada como obsoleta y la nueva clase y método a utilizar es&amp;nbsp;ajaxResponseRenderer.addRender.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/09/peticiones-ajax-en-tapestry.html"&gt;Peticiones Ajax en Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapesty&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-9077954053526241932?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/Q5hyR1DAKzo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/9077954053526241932/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/10/peticiones-ajax-en-tapestry-ii.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/9077954053526241932?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/9077954053526241932?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/Q5hyR1DAKzo/peticiones-ajax-en-tapestry-ii.html" title="Peticiones Ajax en 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/2011/10/peticiones-ajax-en-tapestry-ii.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcBQX08cCp7ImA9WhdbFko.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-2881680602087574015</id><published>2011-10-15T12:34:00.002+02:00</published><updated>2011-10-15T12:54:10.378+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-15T12:54:10.378+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Ejemplo sencillo con JavaCC de un analizador léxico y sintáctico</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;/div&gt;
Hay ocasiones en que necesitamos procesar una determinada expresión, por ejemplo para hacer una búsqueda por una serie de criterios obtenidos de la misma. La forma habitual es hacerlo creando un algoritmo específico más o menos complejo según lo sea la expresión con varios splits, expresiones regulares, condiciones, bucles, etc..., que normalmente resulta en código ofuscado difícil de desarrollar, mantener, entender lo que hace y poco flexible ante cambios. Esta es la primera opción que se nos ocurre pero no es la mejor forma de hacerlo como veremos en esta entrada.&lt;br /&gt;
&lt;br /&gt;
Cuando nos enfrentamos a un problema de procesar una expresión debemos tener en cuenta primeramente dos cosas: cual es su léxico (las palabras que lo forman) y su sintaxis (las reglas que definen el orden del léxico). Más tarde por otra parte para procesar la expresión necesitaremos de acciones léxicas y sintácticas que es código que se ejecutará para realizar las tareas que necesitemos al procesar la expresión.&lt;br /&gt;
&lt;br /&gt;
Para facilitar la tarea existen los compiladores como resultado de la invención de los primeros lenguajes y aunque parecen algo complejo de hacer no lo son tanto como desarrollar un algoritmo específico. &lt;abbr title="Java Compiler Compiler"&gt;&lt;a href="http://javacc.java.net/"&gt;JavaCC&lt;/a&gt;&lt;/abbr&gt; es una herramienta que nos permite definir el léxico de una expresión o lenguaje, la sintaxis del mismo y las acciones léxicas y sintácticas generando posteriormente con la definción de estas cosas una serie de archivos .java con el código fuente de un analizador léxico, sintáctico y otra serie de archivos .java de utilidad para los mismos.&lt;br /&gt;
&lt;br /&gt;
Supongamos que tenemos una aplicación en la que el usuario tiene una caja de búsqueda en la que puede introducir una serie de palabras separadas por espacios, en la que también puede agrupar varias palabas rodeándolas con " y también puede introducir fechas en varios formatos y con distintos separadores para el día, mes y año pudiendo especificar día, mes y año, solo mes y año, solo el mes o solo el año, por ejemplo dd.MMM.yyyy, dd/MMMM/yyyy, MMMM-dd-yyyy, MMM.yyyy, MMM, yyyy, ... Para complicarlo aún más los meses pueden estar en diferentes idiomas. Un ejemplo de expresión podría ser: «"real madrid" enero.2012 febrero.2012 fútbol» en la que intenta buscar elementos relacionados con el real madrid y fútbol y en los meses de enero o febrero de 2012.&lt;br /&gt;
&lt;br /&gt;
Veamos el código fuente de nuestro pequeño compilador cuya misión sera interpretar la expresión de la mejor de las formas y devolver un objeto &lt;a href="http://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/criterion/Criterion.html"&gt;org.hibernate.criterion.Criterion&lt;/a&gt; con el que podremos hacer una búsqueda en Hibernate según los criterios de la expresión. El compilador está dividido en varias partes:&lt;br /&gt;
&lt;br /&gt;
PARSER_BEGIN y PARSE_END: define el nombre de nuestro analizador e incluye métodos de utilidad (en perfecto código Java) que será incluidos en el analizador sintático sin modificar y que podremos usar desde las acciones sintácticas. Los métodos importantes de esta primera parte son los constructores (JavaCC inserta unos pero como vemos podemos definir más), el método main, buildCriterionTermino y buildCriterionFecha que construirán un Criterion cuando el analizador sintáctico detecte un término o fecha respectivamente, la misión principal de nuestro compilador. Estos métodos no tienen mayor complicación son puro código Java. &lt;span class="Apple-style-span" style="color: blue;"&gt;&lt;b&gt;(en azul)&lt;/b&gt;&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
SKIP y TOKEN: esta parte es la que define el analizador léxico con las palabras de nuestra expresión o lenguaje. Ahí están la forma de las fechas, los términos, el día, mes y año, los separadores. Básicamente son una forma de expresiones regulares para definir cada uno de ellos&amp;nbsp;&lt;span class="Apple-style-span" style="color: purple;"&gt;&lt;b&gt;(en morado)&lt;/b&gt;&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
procesarQuery, procesar, termino, fecha: Son propiamente los métodos del analizador sintáctico y van a definir la sintáxis de nuestro lenguaje. procesar contiene una de las partes más importantes ya que es el punto de partida, va cogiendo los tokens proporcionados por el analizador léxico (que genera JavaCC) y determina si es una fecha, término o algo desconocido. Según lo detectado se ejecuta el bloque de código posterior que va entre {} y que constituye una acción sintáctica. Como se ve la acción sintáctica es perfecto código Java y puede usar las variables definidas en el bloque procesar como ct, ft y r. Después de procesar todos los términos de la expresión se ejecuta otra acción sintáctica que agrupa todos los Criterion recogidos en ct, cf en uno solo y que será lo que devuelve el analizador&amp;nbsp;&lt;span class="Apple-style-span" style="color: #38761d;"&gt;&lt;b&gt;(en verde)&lt;/b&gt;&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
En la acción sintáctica de termino tenemos que tener en cuenta que el término puede ser un mes (enero, ...) por lo que se intenta procesar como una fecha con buildCriterionFecha y si devuelve algo es que se trataba de un mes sino se procesará como un termino con buildCriterionTermino.&lt;br /&gt;
&lt;br /&gt;
¿Por qué se trata un elemento DESCONOCIDO? Porque sino el analizador sintáctico daría un excepción al no saber lo que es, teminaría y no devolvería nada. De esta forma conseguimos que si una expresión que no se entiende se ignore y se devuelvan al menos el resto de expresiones en el Criterion. Este será el caso de un término fecha mal expresado como «01.enero/2012» donde mezcla diferentes separadores en la misma fecha. Tal como están definidos los tokens, el analizador léxico no sabría que es.&lt;br /&gt;
&lt;br /&gt;
Y eso es lo principal de nuestro compilador. No es tan complicado hacer uno como podría parecer a priori, sin duda mucho más fácil que hacer un algoritmo específico para ello incluso para una expresión tan simple como la tratada, ahora imagínate una que pueda ser como «(&amp;lt;expr&amp;gt;&lt;expr&gt; or (&lt;/expr&gt;&amp;lt;expr&amp;gt;and (&amp;lt;expr&amp;gt;or&amp;nbsp;&amp;lt;expr&amp;gt;) or&amp;nbsp;&amp;lt;expr&amp;gt;))».&lt;br /&gt;
&lt;expr&gt;&lt;expr&gt;&lt;expr&gt;&lt;expr&gt;&lt;expr&gt;
&lt;br /&gt;
Esta es una de esas herramientas muy útilies y con la cual sabiendo usarla o al menos tener conocimiento de ella nos puede ahorrar mucho tiempo y conseguir hacer las cosas mejor. Además y dado que lo que genera como resultado son una serie de archivos .java podremos utilizarlos en cualquier entorno, como en alguna administración pública cuyo nombre no citaré aquí y en la que no esta permitido usar librerías no homologadas por ellos, dado que se trata de código fuente y que no tiene dependencias sobre otras librerías no tendremos ningún problema en usar JavaCC en casos como este.&lt;br /&gt;
&lt;br /&gt;
Para compilar el compilador podemos hacerlo con ant con la siguiente tarea (también podemos utilizar los propios comandos de JavaCC):&lt;br /&gt;

&lt;br /&gt;
&lt;/expr&gt;&lt;/expr&gt;&lt;/expr&gt;&lt;/expr&gt;&lt;/expr&gt;&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;&amp;lt;javacc target="src/main/java/com/blogspot/elblogdepicodev/jj/Buscador.jj"
 outputdirectory="src/main/java/com/blogspot/elblogdepicodev/jj/buscador"
    javacchome="/home/[user]/javacc-5.0"/&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Los archivos generados serían:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Buscador.java&lt;/li&gt;
&lt;li&gt;BuscadorConstants.java&lt;/li&gt;
&lt;li&gt;BuscadorTokenManager.java&lt;/li&gt;
&lt;li&gt;ParseException.java&lt;/li&gt;
&lt;li&gt;SimpleCharStream.java&lt;/li&gt;
&lt;li&gt;Token.java&lt;/li&gt;
&lt;li&gt;TokenMgrError.java&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;// Buscador.jj
options {
 STATIC = false;
}

&lt;span class="Apple-style-span" style="color: blue;"&gt;PARSER_BEGIN(Buscador)
package com.blogspot.elblogdepicodev.jj.buscador;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.IntegerType;
import org.hibernate.type.Type;

public class Buscador {
 private static final String[] FORMATOS_FECHAS = new String[] { 
  "d/MMM/yyyy", "d/MMMM/yyyy", "MMM/d/yyyy", "MMMM/d/yyyy", "MMM/yyyy", "MMMM/yyyy",
  "d.MMM.yyyy", "d.MMMM.yyyy", "MMM.d.yyyy", "MMMM.d.yyyy", "MMM.yyyy", "MMMM.yyyy",
  "d-MMM-yyyy", "d-MMMM-yyyy", "MMM-d-yyyy", "MMMM-d-yyyy", "MMM-yyyy", "MMMM-yyyy",
  "MMM", "MMMM", "yyyy" 
 };

 private Locale locale;

 public Buscador(Locale locale) {
  this(new StringReader(""), locale);
 }
 
    public Buscador(InputStream is, String encoding, Locale locale) {
  this(is, encoding);
                this.locale = locale;
 }
 
 public Buscador(Reader r, Locale locale) {
  this(r);
        this.locale = locale;
 }

    public static void main(String[] args) throws ParseException, TokenMgrError {
     StringBuffer sb = new StringBuffer();
     for(int i = 0; i &amp;lt; args.length; ++i) {
   if (i &amp;gt; 0) {
    sb.append(" ");
      }
   sb.append(args[i]);
     }

     Buscador parser = new Buscador(new Locale("es"));
     Criterion criterion = parser.procesarQuery(sb.toString());
     System.out.println(criterion);
 }
 
 // Utilidades
 private Criterion or(List&amp;lt;Criterion&amp;gt; criterions) {
  Criterion lhs = null;
  for (Criterion criterion : criterions) {
   if (lhs == null) {
    lhs = criterion;
   } else {
    lhs = Restrictions.or(lhs, criterion);
   }
  }
  return lhs;
 }

 private Criterion and(List&amp;lt;criterion&amp;gt; criterions) {
  Criterion lhs = null;
  for (Criterion criterion : criterions) {
   if (lhs == null) {
    lhs = criterion;
   } else {
    lhs = Restrictions.and(lhs, criterion);
   }
  }
  return lhs;
 }
 
 private Criterion buildCriterionTermino(String term) {
  List&amp;lt;Criterion&amp;gt; coincidencias = new ArrayList&amp;lt;Criterion&amp;gt;();
  
  String t = "%" + term + "%";

  coincidencias.add(Restrictions.ilike("nombre", t));
  coincidencias.add(Restrictions.ilike("ciudad", t));
  coincidencias.add(Restrictions.ilike("direccion", t));

  Criterion criterio = or(coincidencias);
  
  return criterio; 
 }
 
 private Criterion buildCriterionFecha(String term) {
  Criterion criterio = null;
  
  for (int i = 0; i &amp;lt; FORMATOS_FECHAS.length; ++i) {
   String formatoFecha = FORMATOS_FECHAS[i];
   SimpleDateFormat sdf = new SimpleDateFormat(formatoFecha, locale);

   // Fecha
   try {
    Date fecha = sdf.parse(term);

    Calendar calendario = Calendar.getInstance(locale);
    calendario.setTime(fecha);

    switch (i) {
    case 0:
    case 1:
    case 2:
    case 3:
    case 6:
    case 7:
    case 8:
    case 9:
    case 12:
    case 13:
    case 14:
    case 15:
     // Día, mes, año
     criterio = Restrictions.sqlRestriction("(day({alias}.fecha) = ? and month({alias}.fecha) = ? and year({alias}.fecha) = ?)", new Object[] {
       calendario.get(Calendar.DAY_OF_MONTH), calendario.get(Calendar.MONTH) + 1, calendario.get(Calendar.YEAR) }, new Type[] { IntegerType.INSTANCE,
       IntegerType.INSTANCE, IntegerType.INSTANCE });
     break;
    case 4:
    case 5:
    case 10:
    case 11:
    case 16:
    case 17:
     // Mes, año
     criterio = Restrictions.sqlRestriction("(month({alias}.fecha) = ? and year({alias}.fecha) = ?)", new Object[] {
       calendario.get(Calendar.MONTH) + 1, calendario.get(Calendar.YEAR) }, new Type[] { IntegerType.INSTANCE, IntegerType.INSTANCE });
     break;
    case 18:
    case 19:
     // Mes
     criterio = Restrictions.sqlRestriction("month({alias}.fecha) = ?", calendario.get(Calendar.MONTH) + 1, IntegerType.INSTANCE);
     break;
    case 20:
     // Año
     criterio = Restrictions.sqlRestriction("year({alias}.fecha) = ?", calendario.get(Calendar.YEAR), IntegerType.INSTANCE);
     break;
    default:
     assert (false);
     break;
    }
   } catch (java.text.ParseException e) {
   }
   
   if (criterio != null) {
    break;
   }
  }

  return criterio;
 }
 
 private class CriterionInfo {
  public boolean isFecha;
  public Criterion criterio; 
 }
}
PARSER_END(Buscador)&lt;/span&gt;

&lt;span class="Apple-style-span" style="color: purple;"&gt;SKIP : { " " | "\t" | "\n" | "\r" | "\r\n" }

TOKEN : { &amp;lt; #NO_SKIP : ~[" ", "\t", "\r", "\n"] &amp;gt; }
TOKEN : { &amp;lt; #SEP1 : "/" &amp;gt; }
TOKEN : { &amp;lt; #SEP2 : "." &amp;gt; }
TOKEN : { &amp;lt; #SEP3 : "-" &amp;gt; }
TOKEN : { &amp;lt; #SEP : (&amp;lt;sep1&amp;gt; | &amp;lt;sep2&amp;gt; | &amp;lt;sep3&amp;gt;) &amp;gt; }
TOKEN : { &amp;lt; #LETRA : ~[" ", "\t", "\r", "\n", "/", ".", "-"] &amp;gt; }
TOKEN : { &amp;lt; #NUM : ["0"-"9"] &amp;gt; }
TOKEN : { &amp;lt; #MES : (&amp;lt;letra&amp;gt;)+ &amp;gt; }
TOKEN : { &amp;lt; #DIA : (&amp;lt;num&amp;gt; | &amp;lt;num&amp;gt;&amp;lt;num&amp;gt;) &amp;gt; }
TOKEN : { &amp;lt; #ANO : (&amp;lt;num&amp;gt;&amp;lt;num&amp;gt;&amp;lt;num&amp;gt;&amp;lt;num&amp;gt;) &amp;gt; }

TOKEN : { &amp;lt; FECHA : (
 &amp;lt;dia&amp;gt;&amp;lt;sep1&amp;gt;&amp;lt;mes&amp;gt;&amp;lt;sep1&amp;gt;&amp;lt;ano&amp;gt; | &amp;lt;mes&amp;gt;&amp;lt;sep1&amp;gt;&amp;lt;dia&amp;gt;&amp;lt;sep1&amp;gt;&amp;lt;ano&amp;gt; | &amp;lt;mes&amp;gt;&amp;lt;sep1&amp;gt;&amp;lt;ano&amp;gt; | 
 &amp;lt;dia&amp;gt;&amp;lt;sep2&amp;gt;&amp;lt;mes&amp;gt;&amp;lt;sep2&amp;gt;&amp;lt;ano&amp;gt; | &amp;lt;mes&amp;gt;&amp;lt;sep2&amp;gt;&amp;lt;dia&amp;gt;&amp;lt;sep2&amp;gt;&amp;lt;ano&amp;gt; | &amp;lt;mes&amp;gt;&amp;lt;sep2&amp;gt;&amp;lt;ano&amp;gt; |
 &amp;lt;dia&amp;gt;&amp;lt;sep3&amp;gt;&amp;lt;mes&amp;gt;&amp;lt;sep3&amp;gt;&amp;lt;ano&amp;gt; | &amp;lt;mes&amp;gt;&amp;lt;sep3&amp;gt;&amp;lt;dia&amp;gt;&amp;lt;sep3&amp;gt;&amp;lt;ano&amp;gt; | &amp;lt;mes&amp;gt;&amp;lt;sep3&amp;gt;&amp;lt;ano&amp;gt; |
 &amp;lt;num&amp;gt;&amp;lt;num&amp;gt;&amp;lt;num&amp;gt;&amp;lt;num&amp;gt;) &amp;gt; }
TOKEN : { &amp;lt; TERMINO : ("\""(~["\""])+"\"" | (&amp;lt;letra&amp;gt;)+) &amp;gt; }
TOKEN : { &amp;lt; DESCONOCIDO : (&amp;lt;no_skip&amp;gt;)+ &amp;gt; }&lt;/span&gt;

&lt;span class="Apple-style-span" style="color: #38761d;"&gt;Criterion procesarQuery(String query) :
{
 Criterion criterio = null;

 ReInit(new StringReader(query));
}
{
 criterio = procesar()
 {
  return criterio;
 }
}

Criterion procesar() :
{
 List&amp;lt;Criterion&amp;gt; ct = new ArrayList&amp;lt;Criterion&amp;lt;();
 List&amp;lt;Criterion&amp;gt; cf = new ArrayList&amp;lt;Criterion&amp;lt;();

 Criterion criterio = null;
 CriterionInfo criterioInfo = null;
 Token t = null;
}
{
 (
  criterio = fecha()
  {
   if (criterio != null) {
    cf.add(criterio);
   }
   //System.out.println(criterio);
  }
 |
  criterioInfo = termino() 
  {
   criterio = criterioInfo.criterio;
   if (criterio != null) {
    if (criterioInfo.isFecha) {
     cf.add(criterio);
    } else {
     ct.add(criterio);
    }
   }
   //System.out.println(criterio);
  }
 |
  t = &amp;lt;desconocido&amp;gt;
  {
   //System.out.println(t.image);
  }
 )*
 &amp;lt;eof&amp;gt;
 {
  List&amp;lt;Criterion&amp;gt; r = new ArrayList&amp;lt;Criterion&amp;gt;();
  if (!ct.isEmpty()) {
   r.addAll(ct);
  }
  if (!cf.isEmpty()) {
   r.add(or(cf));
  }
  return (r.isEmpty())?null:and(r);
 } 
}

CriterionInfo termino() :
{
 Token t = null;
}
{
 t = &amp;lt;termino&amp;gt;
 { 
  //System.out.println(t.image);
  String term = t.image;
  CriterionInfo ci = new CriterionInfo();
   
  // Comprobar si se trata de un mes
  ci.criterio = buildCriterionFecha(term);
  
  if (ci.criterio != null) {
   ci.isFecha = true;
  } else {
   ci.isFecha = false;

   if (term.startsWith("\"") &amp;amp;&amp;amp; term.endsWith("\"")) {
    term = term.substring(1, term.length() - 1);
   }
   ci.criterio = buildCriterionTermino(term);
  } 
  return ci;
 }
}

Criterion fecha() :
{
 Token t = null;
}
{
 t = &amp;lt;fecha&amp;gt;
 { 
  String term = t.image;
  return buildCriterionFecha(term);
 }
}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://javacc.java.net/"&gt;http://javacc.java.net/&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.lpsi.eui.upm.es/webcomp/jgperez/java/IntrodJavaCC.pdf"&gt;http://www.lpsi.eui.upm.es/webcomp/jgperez/java/IntrodJavaCC.pdf&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-2881680602087574015?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/1B60TBxzfDM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/2881680602087574015/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/10/ejemplo-sencillo-con-javacc-de-un.html#comment-form" title="1 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2881680602087574015?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2881680602087574015?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/1B60TBxzfDM/ejemplo-sencillo-con-javacc-de-un.html" title="Ejemplo sencillo con JavaCC de un analizador léxico y sintáctico" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/10/ejemplo-sencillo-con-javacc-de-un.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcMQnk8eyp7ImA9WhdbEU0.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7649503307031558884</id><published>2011-10-07T17:46:00.000+02:00</published><updated>2011-10-08T23:08:03.773+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-08T23:08:03.773+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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="programación" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta código" /><title>Configuración de entorno en Java con ayuda de Groovy</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;img alt="Groovy" border="0" height="98" src="http://1.bp.blogspot.com/-8zEd2dMvhXs/TGUMtyhkvpI/AAAAAAAAAdI/j0-zeOq44o4/s200/groovy.png" title="Groovy" width="200" /&gt;&lt;/div&gt;
Una necesidad habitual en prácticamente todas las aplicaciones y proyectos es tener una forma de poder configurar una aplicación en función del entorno en el que se vaya a ejecutar. Cosas habituales que cambian dependiendo del entorno son la configuración de logging, conexión a la base de datos, tal vez la configuración de hibernate si lo usamos, parámetros, etc... Habitualmente necesitaremos un entorno con su configuración para la máquina en la que desarrolla cada programador del proyecto que será el entorno de desarrollo, también es habitual tener un entorno de pruebas independiente del entorno de cada desarrollador y el entorno de producción que es donde se ejecuta la aplicación.&lt;br /&gt;
&lt;br /&gt;
La configuración del multientorno la podemos la podemos hacer de diferentes formas, mediante archivos de propiedades, con xml u otras formas. Aqui vamos a ver como hacerlo con la ayuda de groovy de una forma sencilla sin tener que pelearnos con procesar la forma del archivo, que con xml puede llegar a ser laborioso y con archivos .properties un tanto limitado en cuanto a posibilidades.&lt;br /&gt;
&lt;br /&gt;
Veámos primero lo que sería el archivo de configuración:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;[
    entorno: 'desarrollo',
    
	//
	valores: [
		'valor1', 
		'valor2', 
		'valor3'
	] as String[],

	// Redefinición para configuración para entornos
    entornos: [
        desarrollo: [
		    log4j:      '/cfg/log4j-desarrollo.properties',
    		hibernate:  '/cfg/hibernate-desarrollo.cfg.xml',
		    quartz:     '/cfg/quartz-desarrollo.properties',
		],
        pruebas: [
			log4j:      '/cfg/log4j-pruebas.properties',
			hibernate:  '/cfg/hibernate-pruebas.cfg.xml',
			quartz:     '/cfg/quartz-pruebas.properties',
		],
        produccion: [
			log4j:      '/cfg/log4j-produccion.properties',
			hibernate:  '/cfg/hibernate-produccion.cfg.xml',
			quartz:     '/cfg/quartz-produccion.properties',

			valor: [
				'valor-produccion1', 
				'valor-produccion2', 
				'valor-produccion3'
			] as String[],
		]
    ]
]&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
El archivo de configuración es código groovy que define un mapa con una serie de propiedades y valores. La propiedad entorno indica el entorno que será usado en tiempo de ejecución por la aplicación. La propiedad entornos tiene las propiedades específicas de cada entorno. Las propiedad «valores» está definda de forma global independiente de entorno y es común para los entornos de desarrollo y pruebas pero que se redefine para el entorno de producción.

&lt;br /&gt;
&lt;br /&gt;
La clase Entorno nos permitirá procesar el archivo de configuración groovy y la clase Utilidades tiene unos métodos de utilidad para facilitar la tarea.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;// Entorno.java
package com.blogspot.elblogdepicodev.misc;

import java.io.InputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Entorno {

	private static Logger logger = LoggerFactory.getLogger(Entorno.class);
	
	public enum Ent {
		DESARROLLO, PRUEBAS, PRODUCCION
	}
	
	public static final String PROP_LOG4J = "log4j";
	public static final String PROP_HIBERNATE = "hibernate";
	public static final String PROP_QUARTZ = "quartz";

	private static Object conf;
	private static Ent entorno;
	private static Object configuracion;
	
	public static void initialize(Object conf) throws Exception {
		initialize(conf, null);
	}
	
	public static void initialize(Object conf, Ent ent) throws Exception {
		Entorno.conf = conf;
		Entorno.entorno = (ent != null)?ent:Ent.valueOf(((String) getPropiedadGlobal("entorno")).toUpperCase());
		Entorno.configuracion = getPropiedadGlobal("entornos." + Entorno.entorno.toString().toLowerCase());
		
		logger.debug("Entorno inicializado (" + ent + ")");
	}
	
	public static Object getPropiedad(String propiedad) {
		try {
			String p = propiedad.replaceAll("\\.", "?.");
			// Ver si es una propiedad del entorno
			Object o = getPropiedadEntorno(p);
			if (o == null) {
				// No es una propiedad del entorno, ver si es una propiedad global
				o = getPropiedadGlobal(p);
			}
			return o;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String getString(String propiedad) {
		return (String) getPropiedad(propiedad);
	}
	
	public static InputStream getResourceAsStream(String propiedad) {
		return (InputStream) Entorno.class.getResourceAsStream(getString(propiedad));
	}
	
	public static Ent getEntorno() {
		return entorno;
	}
	
	public static boolean isEntorno(Ent e) {
		return entorno.equals(e);
	}
	
	public static boolean isDesarrollo() {
		return isEntorno(Ent.DESARROLLO);
	}
	
	public static boolean isPruebas() {
		return isEntorno(Ent.PRUEBAS);		
	}
	
	public static boolean isProduccion() {
		return isEntorno(Ent.PRODUCCION);
	}	
	
	public static Object getPropiedadGlobal(String propiedad) throws Exception {
		return Utilidades.groovy("conf." + propiedad, Utilidades.map("conf", conf));
	}
	
	public static Object getPropiedadEntorno(String propiedad) throws Exception {
		return Utilidades.groovy("configuracion." + propiedad, Utilidades.map("configuracion", configuracion));
	}
}

// Utilidades.java
package com.blogspot.elblogdepicodev.misc;

import groovy.text.SimpleTemplateEngine;
import groovy.text.TemplateEngine;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.blogspot.elblogdepicodev.misc.Entorno.Ent;

public class Utilidades {

	private static Logger logger = LoggerFactory.getLogger(Utilidades.class);

	private static ScriptEngine engine;

	static {
		ScriptEngineManager manager = new ScriptEngineManager();
		engine = manager.getEngineByName(Constantes.GROOVY_ENGINE_NAME);
	}

	public static void initializeEntorno() throws Exception {
		initializeEntorno(null);
	}

	public static void initializeEntorno(Ent ent) throws Exception {
		StringBuffer sb = new StringBuffer();

		InputStream is = Utilidades.class.getResourceAsStream("/cfg/Configuracion.groovy");
		Reader r = new InputStreamReader(is);

		char[] b = new char[4096];
		int i = r.read(b);
		while (i != -1) {
			sb.append(b, 0, i);
			i = r.read(b);
		}
		Object configuracion = Utilidades.groovy(sb.toString(), Collections.EMPTY_MAP);
		Entorno.initialize(configuracion, ent);
	}

	public static Object groovy(String script, Map bindings) throws ScriptException {
		Bindings b = engine.createBindings();
		if (bindings != null) {
			b.putAll(bindings);
		}
		return engine.eval(script, b);
	}
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Los métodos importantes de esta clase son el initializeEntorno de clase Utilidades que se encarga de leer el archivo de configuración, evaluarlo como una expresión groovy y pasárselo al método initialize de la clase Entorno. La llamada a initializeEntorno la haremos cuando se arranque la aplicación posiblemente en un ContextLitener si se trata de una aplicación web. Una vez tengamos en entorno inicializado podremos llamar a getPropiedad de Entorno para obtener el valor de una propiedad, este método primeramente buscará entre la propiedades específicas entorno en ejecución y si no existe en él buscará entre las propiedades globales, devolverá lo primero en lo que encuentre algo.&lt;br /&gt;
&lt;br /&gt;
Con estas dos clases tenemos una solución sencilla pero a la vez muy potente de configuración de entornos. Dado que las propiedades las estamos de definiendo con groovy las propiedades devueltas no será solo datos String que luego tendremos que parsear en nuestra aplicación sino que con la ayuda de groovy las propiedades devueltas podrán ser objetos, Double, Long, Date, listas, mapas, etc .... Y esta no es la unica forma de definir el archivo de configuración podríamos hacer que fuese un objeto en vez de un mapa de propiedades y podríamos definir métodos con alguna lógica y llamarlos al obtener una propiedad. ¡Las posibilidades son muchas para estas pocas líneas de código!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-7649503307031558884?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/mQ3E8Nzgcro" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7649503307031558884/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/10/configuracion-de-entorno-en-java-con.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7649503307031558884?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7649503307031558884?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/mQ3E8Nzgcro/configuracion-de-entorno-en-java-con.html" title="Configuración de entorno en Java con ayuda de Groovy" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/10/configuracion-de-entorno-en-java-con.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcHSHk5cSp7ImA9WhdaEk8.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-4324775904217821246</id><published>2011-09-30T18:59:00.002+02:00</published><updated>2011-10-21T20:00:39.729+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-21T20:00:39.729+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="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><title>Peticiones Ajax en Tapestry</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;br /&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;
Tapestry posee un excelente soporte para trabajar con Ajax incluso llegando al punto de no ser necesario escribir ni una sola línea de javascript para hacer este tipo de peticiones. Aquellos componentes de Tapestry que poseen el parámetro zone (como por ejemplo &lt;a href="http://tapestry.apache.org/current/tapestry-core/ref/org/apache/tapestry5/corelib/components/ActionLink.html"&gt;ActionLink&lt;/a&gt;, &lt;a href="http://tapestry.apache.org/current/tapestry-core/ref/org/apache/tapestry5/corelib/components/EventLink.html"&gt;EventLink&lt;/a&gt; o &lt;a href="http://tapestry.apache.org/current/tapestry-core/ref/org/apache/tapestry5/corelib/components/Form.html"&gt;Form&lt;/a&gt;, ...) permiten actualizar un fragmento de la página (o varios a la vez) mediante una petición Ajax. Además de utilizar un componte que posea un parámetro zone hay que definir las zonas, mediante el componente &lt;a href="http://tapestry.apache.org/current/tapestry-core/ref/org/apache/tapestry5/corelib/components/Zone.html"&gt;Zone&lt;/a&gt;, que pueden ser actualizadas.&lt;br /&gt;
&lt;br /&gt;
En &lt;a href="http://jumpstart.doublenegative.com.au/jumpstart/examples/ajax/actionlink"&gt;el ejemplo Ajax ActionLink&lt;/a&gt; de la &lt;a href="http://jumpstart.doublenegative.com.au/jumpstart/"&gt;aplicación demostración Jumpstart&lt;/a&gt; puede verse un ejemplo en funcionamiento de actualización de un fragmento de la página, tambien puede verse el código fuente de los elementos implicados.&lt;br /&gt;
&lt;br /&gt;
Una de las cosas que hay que destacar es lo sencillo que es pasar de una aplicación no-ajax a una ajax si esto se ajusta a lo que necesitamos, para ello basta usar los parametros zone de los componentes y definir las zonas en la propia página, hay que notar que no es necesario separar ese contenido de la zonas en otro archivo para devolverlo únicamete cuando se haga la petición ajax todo está en un único archivo y Tapestry se encarga de devolver únicamente el contenido de la zona cuando esta vaya a a ser actualizada en una petición Ajax.&lt;br /&gt;
&lt;br /&gt;
El actualizar fragmentos de una página con el contenido html generado por una zona cubre la mayoría de los casos en los que es necesario trabajar con Ajax, sin embargo, en alguna ocasión podenos necesitar trabajar a más bajo nivel haciendo nosotros mismos la petición Ajax y devolviendo datos en formato json en el servidor que luego procesaremos en el cliente para tratarlos, en el siguiente ejemplo se ve como es. El componente hace una petición ajax sobre el evento onGetColores del mismo al cabo de 5 segundos de cargarse la página en la que es incluido. En el cliente hace un alert para cada uno de los elementos devueltos en el evento.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;//Colores.java
package com.blogspot.elblogdepicodev.tapestry.components;

import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONArray;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;

@Import(library = { "Colores.js" })
public class Colores {

    @Environmental
    private JavaScriptSupport javaScriptSupport;
    
    @Inject
    private ComponentResources componentResources;

    Object onGetColores() {
 return new JSONArray("Rojo",&amp;nbsp;"Verde",&amp;nbsp;"Azul",&amp;nbsp;"Negro");
    }

    protected void afterRender(MarkupWriter writer) {
 String link = componentResources.createEventLink("getColores").toAbsoluteURI();
        
 JSONObject spec = new JSONObject();
 spec.put("link", link);

        javaScriptSupport.addInitializerCall("colores", spec);
    }
}

// Colores.js
function Colores(spec) {
 this.link = spec.link;

 var sthis = this;
 setTimeout(function() { sthis.getColores(); }, 5000);
}
Colores.prototype.getColores = function() {
 $j.ajax({
  url: this.link,
  success: function(data) {
                    $j.each(data, function(index, color) { 
                        alert(color); 
                    });
                }
 });
}

Tapestry.Initializer.colores = function(spec) {
 new Colores(spec);
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
El javascript del ejemplo utiliza la librería jQuery para hacer la petición Ajax y la referencia a ella con la etiqueta script habría que incluirlo en la página, $j es una variable con una referencia a ella.&lt;br /&gt;
&lt;br /&gt;
Todo realmente sencillo, en la mayoría de los casos no será necesario que trabajemos a bajo nivel con las peticiones Ajax. Otra cosa más que hace que trabajar con Tapestry sea muy agradable.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2011/10/peticiones-ajax-en-tapestry-ii.html"&gt;Peticiones Ajax en Tapestry (II)&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/ajax-and-zones.html"&gt;http://tapestry.apache.org/ajax-and-zones.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://tapestry.apache.org/javascript.html"&gt;http://tapestry.apache.org/javascript.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://jumpstart.doublenegative.com.au/home.html"&gt;http://jumpstart.doublenegative.com.au/home.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://jumpstart.doublenegative.com.au/jumpstart/"&gt;http://jumpstart.doublenegative.com.au/jumpstart/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-4324775904217821246?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/Qx0zLVHnAAY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/4324775904217821246/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/09/peticiones-ajax-en-tapestry.html#comment-form" title="3 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4324775904217821246?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4324775904217821246?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/Qx0zLVHnAAY/peticiones-ajax-en-tapestry.html" title="Peticiones Ajax en 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>3</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/09/peticiones-ajax-en-tapestry.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4EQ3o7cSp7ImA9WhRWEEg.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7263173647309509746</id><published>2011-09-23T19:32:00.001+02:00</published><updated>2011-12-28T08:21:42.409+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-28T08:21:42.409+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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="programación" /><title>Comparar cadenas en Java ignorando tildes</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="106" /&gt;&lt;/div&gt;
A veces es necesario comparar cadenas de texto (String) para realizar ordenaciones de elementos. Pero en esta tarea aparentemente sencilla probablemente necesitemos ignorar mayúsculas y minúsculas y las palabras con tildes en el caso del Español y si necesitamos la misma funcionalidad de ordenación en otros idiomas aún se puede complicar más ya que pueden utilizar más caracteres que pueden presentar problemas (ã, ä, å, â, æ, &lt;a href="http://www.atm.ox.ac.uk/user/iwi/charmap.html"&gt;mapa de caracteres&lt;/a&gt;).

Una solución que se suele aplicar habitualmente es hacer un reemplazo de esos caracteres (á, ã, ä, å, â, ...) para igualar diferentes caracteres de las cadenas (a). Pero para ello hay que conocer todos esos caracteres para cada idioma lo que puede ser un problema.

Para facilitar la tarea de ordenar cadenas Java dispone de la clase &lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/Collator.html"&gt;Collator&lt;/a&gt; teniendo en cuenta ciertas reglas del idioma en concreto.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;List paises = ...;
Locale locale = ...;

&lt;b&gt;Collator c = Collator.getInstance(locale);
c.setStrength(Collator.PRIMARY);&lt;/b&gt;

Collections.sort(paises, new CollatorComparator(c));
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Con una clase propia, CollatorComparator, que implementa la interfaz &lt;a href="http://download.oracle.com/javase/7/docs/api/java/util/Comparator.html"&gt;Comparator&lt;/a&gt;&amp;nbsp;podemos hacer la ordenación de una lista de cadenas:

&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;import java.util.Comparator;
import java.text.Collator;

public class CollatorComparator implements Comparator {
 
 private Collator collator;

 public CollatorComparator(Collator collator) {
  this.collator = collator;
 }

 public int compare(Object o1, Object o2) {
  return collator.compare(o1, o2);
 }

 public boolean equals(Object obj) {
  return false;
 }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Algo importante es la «fuerza» del comprador, que permite ajustar cuales son los caracteres que se consideran diferentes en la comparación. Disponemos de las siguientes posibilidades:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/Collator.html#PRIMARY"&gt;PRYMARY&lt;/a&gt;: Considera como distintos los distintos caracteres («a» vs «b»)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/Collator.html#SECONDARY"&gt;SECONDARY&lt;/a&gt;: Considera como distintos diferentes formas acentuadas («a» vs «á»)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/Collator.html#TERTIARY"&gt;TERTIARY&lt;/a&gt;: Considera como diferentes mayúsculas y minúsculas («a» vs «A»)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/Collator.html#IDENTICAL"&gt;IDENTICAL&lt;/a&gt;: Solo se consideran idénticos («a» vs «a»)&lt;/li&gt;
&lt;/ul&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://download.oracle.com/javase/7/docs/api/java/util/Comparator.html"&gt;java.util.Comparator&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/Collator.html"&gt;java.text.Collator&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/RuleBasedCollator.html"&gt;java.text.RuleBasedCollator&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-7263173647309509746?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/XNNnW-Sd1dg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7263173647309509746/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/09/comparar-cadenas-en-java-ignorando.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7263173647309509746?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7263173647309509746?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/XNNnW-Sd1dg/comparar-cadenas-en-java-ignorando.html" title="Comparar cadenas en Java ignorando tildes" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/09/comparar-cadenas-en-java-ignorando.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkEBQ3o9eSp7ImA9WhdUF04.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-2416500921046425698</id><published>2011-09-09T21:09:00.000+02:00</published><updated>2011-10-04T16:30:52.461+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-04T16:30:52.461+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="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><title>Componente AjaxSpinner para Tapestry 5</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;
Hoy en día las aplicaciones web que hace peticiones de forma asíncrona con AJAX son la mayoría ya que es una técnica que permite obtener únicamente los datos que se necesitan sin tener que hacer una cargar entera de la página web en el navegador. Esta técnica hace que las peticiones al devolver únicamente los datos necesarios (y no toda la página) y los recursos para procesar la petición sean menos, también hace que el resultado de las peticiones sean más pequeños con lo que el tiempo de carga también se reduce al generar menos tŕafico de red, asi mismo el usuario nota un aumento de tiempo de respuesta. También hace que en ciertos casos las aplicaciones sean más fáciles de desarrollar por no tener que tratar en el servidor en cada petición el estado concreto de cada cliente.&lt;br /&gt;
&lt;br /&gt;
Por la naturaleza de las peticiones, asíncronas y ejecutandose en segundo plano, es recomendable mostrar al usuario algún tipo de notificación que informe a este de que hay una petición AJAX en curso de tal forma que pueda saber que se está haciendo algo y que se ha completado.&lt;br /&gt;
&lt;br /&gt;
En el siguiente ejemplo voy a desarrollar un componente para Apache Tapestry 5 que implemente esta funcionalidad de tal forma que siempre que se esté realizando una petición AJAX se muestre un pequeño tooltip del mismo estilo del que existe en gmail o google reader pero con los puntos suspentivos finales animados (de «.» a «..» a «...»  a «....» y otra vez al inicio de «.»). En la vesión 5.2 de Tapestry aún se usa prototype y las peticiones AJAX que inicia Tapestry las realiza con esta librería. Por suerte siempre que Prototype inicia una petición o alguna finaliza lanza &lt;a href="http://www.prototypejs.org/api/ajax/responders"&gt;dos eventos&lt;/a&gt; informando de ello. Por lo que nuestra tarea será registrar un par de funciones propias para estos dos eventos con la lógica que queremos implementar.&lt;br /&gt;
&lt;br /&gt;
El código Java del componente es el siguiente, lo importante está en el método afterRender donde se genera el código javascript de inicialización que crea la instancia del objeto javascript AjaxSpinner cuando se carga la página:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;&lt;span class="Apple-style-span"&gt;package &lt;/span&gt;com.blogspot.elblogdepicodev.tapestry.components;
&lt;span class="Apple-style-span"&gt;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;

@Import(stylesheet = { "AjaxSpinner.css" }, library = { "AjaxSpinner.js" })
public class AjaxSpinner {

    @Environmental
    private JavaScriptSupport javaScriptSupport;
    
    @Inject
    private ComponentResources componentResources;

    protected void afterRender(MarkupWriter writer) {
        String id = componentResources.getId();

        JSONObject spec = new JSONObject();
        spec.put("id", id);
        
        javaScriptSupport.addInitializerCall("ajaxSpinner", spec);
    }
}&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
La plantilla del componente se encarga de generar el html con las capas que van a contener los tooltips, uno para indicar que se está realizando una petición, otro para indicar que aún está en funcionamiento si la petición tarda un rato y otro para indicar que la petición está tardando más de lo normal. Se mostrará una capa u otra dependiendo del tiempo que lleve realizandose. El código de la plantilla AjaxSpinner.tml del componente es:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;
&amp;lt;t:container xmlns:p="tapestry:parameter" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns="http://www.w3.org/1999/xhtml"&amp;gt;
&amp;lt;div class="ajax-spinner" id="${componentResources.id}1" t:type="@Any"&amp;gt;
    Cargando...
&amp;lt;/div&amp;gt;
&amp;lt;div class="ajax-spinner" id="${componentResources.id}2" t:type="@Any"&amp;gt;
    Aún en funcionamiento. Cargando...
&amp;lt;/div&amp;gt;
&amp;lt;div class="ajax-spinner ajax-spinner-tardando" id="${componentResources.id}X" t:type="@Any"&amp;gt;
    Está tardando demasiado...
&amp;lt;/div&amp;gt;
&amp;lt;/t:container&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
El código javascript&amp;nbsp;(AjaxSpinner.js)&amp;nbsp;para el componente es:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;function AjaxSpinner(spec) {
	this.id = spec.id;
	this.t1 = null;
	this.t2 = null;
	this.tX = null;
	this.peZ = null;
	
	this.d1 = $j('#' + this.id + '1');
	this.d2 = $j('#' + this.id + '2');
	this.dX = $j('#' + this.id + 'X');
	
	this.d1.css('margin-left', -1 * (this.d1.width() / 2));
	this.d2.css('margin-left', -1 * (this.d2.width() / 2));
	this.dX.css('margin-left', -1 * (this.dX.width() / 2));
	
	var sthis = this;
	
	Ajax.Responders.register({
		onCreate: function() {
			sthis.clear();
			sthis.t1 = setTimeout(function() { sthis.timeout1(); }, 500);
			sthis.t2 = setTimeout(function() { sthis.timeout2(); }, 15000);
			sthis.tX = setTimeout(function() { sthis.timeoutX(); }, 45000);
			sthis.peZ = new PeriodicalExecuter(function(pe) { sthis.timeoutZ(); }, 1);
		},
		onComplete: function() {
			if (Ajax.activeRequestCount == 0) {
				sthis.clear();
			}
		}
	});
}

AjaxSpinner.prototype.timeout1 = function() {
	this.d1.show();
	this.d2.hide();
	this.dX.hide();
}

AjaxSpinner.prototype.timeout2 = function() {
	this.d1.hide();
	this.d2.show();
	this.dX.hide();
}

AjaxSpinner.prototype.timeoutX = function() {
	this.d1.hide();
	this.d2.hide();
	this.dX.show();
}

AjaxSpinner.prototype.timeoutZ = function() {
	var e = $j('div[id^=' + this.id + ']:visible');
	var t = e.html().trim();
	if (t.endsWith('....')) {
		t = t.substring(0, t.length - 3);
	} else {
		t += '.';
	}
	e.html(t);
}

AjaxSpinner.prototype.clear = function () {
	if(this.t1) clearTimeout(this.t1);
	if(this.t2) clearTimeout(this.t2);
	if(this.tX) clearTimeout(this.tX);
	if(this.peZ) this.peZ.stop();
	this.d1.hide();
	this.d2.hide();
	this.dX.hide();
}

Tapestry.Initializer.ajaxSpinner = function(spec) {
	new AjaxSpinner(spec);
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Y los estilos CSS (AjaxSpinner.css) para dar a los tooltips el mismo aspecto que los de gmail y google reader:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;div.ajax-spinner {
	display: none;
	position: fixed;
	padding: 5px;
	top: 0px;
	left: 50%;
	text-align: center;
	background-color: #fff1a8;
	border-bottom-left-radius: 5px;
	border-bottom-right-radius: 5px;
	z-index: 9999;
	vertical-align: middle;
}

div.ajax-spinner-tardando {
	background-color: #f8baba;
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Una vez desarrollado el componente lo único que tendríamos que hacer es hacer uso del componente en la plantilla de la página donde queramos usarlo, por ejemplo:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;
&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;${titulo}&amp;lt;/title&amp;gt;

    &amp;lt;link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold,bolditalic"&amp;gt;&amp;lt;/link&amp;gt;
    &amp;lt;link t:type="any" rel="shortcut icon" href="context:/images/favicon.ico"&amp;gt;&amp;lt;/link&amp;gt;
    &amp;lt;meta name="description" content="${message:descripcion-app}"/&amp;gt;
    &amp;lt;meta name="keywords" content="${message:keywords-app}"/&amp;gt;
    
    &amp;lt;t:extension-point id="head"&amp;gt;
    &amp;lt;/t:extension-point&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &lt;b&gt;&amp;lt;t:ajaxspinner t:id="principal"/&amp;gt;&lt;/b&gt;

    &amp;lt;div class="body"&amp;gt;
        &amp;lt;t:extension-point id="cabecera"&amp;gt;
        &amp;lt;/t:extension-point&amp;gt;
        
        &amp;lt;t:extension-point id="menu"&amp;gt;
        &amp;lt;/t:extension-point&amp;gt;
        
        &amp;lt;t:extension-point id="cuerpo"&amp;gt;
        &amp;lt;/t:extension-point&amp;gt;

        &amp;lt;t:extension-point id="pie"&amp;gt;
        &amp;lt;/t:extension-point&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
El resultado cuando se hace un petición AJAX que tarda bastante tiempo es:&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/-Enx47Bk5SMM/Tmpfiwhmw9I/AAAAAAAAA-g/mQgvsJmbylc/s1600/AjaxSpinner1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-Enx47Bk5SMM/Tmpfiwhmw9I/AAAAAAAAA-g/mQgvsJmbylc/s1600/AjaxSpinner1.png" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-K1PIa5Z6-Zs/TmpfjA7lUnI/AAAAAAAAA-k/UmridaGqnCw/s1600/AjaxSpinner2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-K1PIa5Z6-Zs/TmpfjA7lUnI/AAAAAAAAA-k/UmridaGqnCw/s1600/AjaxSpinner2.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-Ozq1URnDFM0/TmpfjY6rnUI/AAAAAAAAA-o/RDRGmlaJhV4/s1600/AjaxSpinner3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-Ozq1URnDFM0/TmpfjY6rnUI/AAAAAAAAA-o/RDRGmlaJhV4/s1600/AjaxSpinner3.png" /&gt;&lt;/a&gt;&lt;/div&gt;
Tal vez te preguntes... ¿solo hace falta poner &amp;lt;t:ajaxspinner t:id="principal"/&amp;gt; para usarlo? ¿y el archivo .js con el código javascript no hay que meterlo en el head de la plantilla? ¿y la instanciación del AjaxSpinner en javascript? ¿y el CSS tampoco hay que meterlo en el head? No, no hace falta saber que el componente hace uso de un archivo de javscript ni la hora de estilos que necesita ni saber como se inicializa de eso ya tiene conocimiento tapestry por la definición del componente (mediante la anotación Import) y se encarga de incluir todo y de hacerlo eficientemente agregando el contenido de los archivos en uno solo. También el propio componente se encarga de generar el código javascript que necesita para inicalizarse.&lt;br /&gt;
&lt;br /&gt;
Esta es una de las grandes características de los componentes tapestry ya que como véis lo único que hace falta es usarlo y pasarle los parametros necesarios (aunque en esta caso no tiene ninguno), no hace falta saber como funciona internamente, los estilos y que javascript necesita. Los componentes de tapestry permite abstraernos enormemente de sus detalles y eso es bueno, muy bueno ¿no crees?.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-2416500921046425698?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/z2Q1U--9EVI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/2416500921046425698/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/09/componente-ajaxspinner-para-apache.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2416500921046425698?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/2416500921046425698?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/z2Q1U--9EVI/componente-ajaxspinner-para-apache.html" title="Componente AjaxSpinner para Tapestry 5" /><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/2011/09/componente-ajaxspinner-para-apache.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4BQnc8eip7ImA9WhdWEEw.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-3947454537769489965</id><published>2011-09-02T20:16:00.001+02:00</published><updated>2011-09-03T04:45:53.972+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-03T04:45:53.972+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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" /><title>Integración continua con Jenkins</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: left;"&gt;
&lt;img alt="Jenkins" border="0" height="63" src="http://1.bp.blogspot.com/-VDA-zXScjY8/TmEXmNUTE4I/AAAAAAAAA-M/9Ox1D_VDmvQ/s200/jenkins.png" title="Jenkins" width="200" /&gt;&lt;/div&gt;
Jenkins es una herramienta de &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;integración continua&lt;/a&gt; (CI) que permite realizar tareas periódicas y automáticamente como generar el archivo del proyecto o ejecutar los casos de prueba. Usar una herramienta de integración contínua tiene varios beneficios como detectar problemas de forma temprana haciendo que sean más fáciles de resolver y automatizar procesos repetitivos que nos liberan de tiempo. Todo ello en un entorno independiente de los de desarrollo.&lt;br /&gt;
&lt;br /&gt;
Su instalación y uso es muy sencillo basta con descargar u archivo .war y arrancarlo con:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;java -jar jenkins.war&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
O desplegarlo en un contenedor de aplicaciones como Tomcat ya que es una aplicación web java. Su configuración se puede hacer completamente desde la interfaz de usuario que proporciona. Algunas otras características de Jenkins son:&lt;br /&gt;
&lt;br /&gt;
- Puede proporcionar el conjunto de cambios realizados en un commit al repositorio de control de versiones como CVS/Subversion.	&lt;br /&gt;
- Proporciona enlaces permanentes para porder utilizarlos en otras herramientas.&lt;br /&gt;
- Tiene nitificiones por RSS, correo electrónico y mensajería instantánea.&lt;br /&gt;
- Informes de las pruebas JUnit y TestNG.&lt;br /&gt;
- Puede distribuir las compilaciones y pruebas entre varias máquinas.&lt;br /&gt;
- Y una de sus mejores características, el soporte para plugins que le añaden funcionalidades. Y hay un montón de ellos para diversas tareas.&lt;br /&gt;
&lt;br /&gt;
Jenkins no necesita una base de datos para funcionar lo que simplifica su configuración pero necesita guardar datos y lo hace por defecto en el directorio ~/.jenkins. Para actualizar a una nueva versión de jenkins basta con reeemplazar el archivo jenkins.war por el nuevo. Para hacer una copia de seguridad de los datos o copiarlos a otra máquina basta con copiar el directorio ~/.jenkins.&lt;br /&gt;
&lt;br /&gt;
Nada más arrancar Jenkins y acceder con el navegador a la dirección http://localhost:8080/jenkins veremos la siguiente página:&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/-PYreqeSwdCM/TmEWWSiH9HI/AAAAAAAAA-E/4Z9oY98OKLg/s1600/Panel+de+control.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="90" src="http://2.bp.blogspot.com/-PYreqeSwdCM/TmEWWSiH9HI/AAAAAAAAA-E/4Z9oY98OKLg/s200/Panel+de+control.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
Vamos a ver como configurar una tarea para sea ejecutada por jenkins cada cierto tiempo y vamos a ver como instalar un plugin, en concreto el plugin de git.&lt;br /&gt;
&lt;br /&gt;
Para crear una tarea que haga el trabajo que queramos pulsaremos el enlace «Crear nueva Tarea», le daremos un nombre a la tarea y seleccionaremos la opción «Crear un proyecto de estilo libre». Al configurar la tarea veremos un montón de opciones pero por suerte de cada una de ellas tiene una «?» con la que podremos obtener ayuda. Algunas importantes son la configuración para que jenkins obtenga el código fuente de la aplicación. El apartado de disparadores de las ejecuciones por una expresión cron o por consulta de repositorio de control de versiones. El apartado de Ejecutar para lanzar la tarea que deseemos. Y las acciones que queramos que haga jenkins despues de ejecutar la tarea como enviar correos electrónicos o ejecutar otros proyectos. En la captura de pantalla he configurado una tarea (o proyecto) para que se ejecute cada minuto lanzando el comando date del sistema (en este caso de un sistema Linux).&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/-5lCkLjNhouM/TmEWVYjWG8I/AAAAAAAAA-A/DrrEhSGToek/s1600/Configuraci%25C3%25B3n+tarea.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="155" src="http://2.bp.blogspot.com/-5lCkLjNhouM/TmEWVYjWG8I/AAAAAAAAA-A/DrrEhSGToek/s200/Configuraci%25C3%25B3n+tarea.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-LMixfX-5lvc/TmEZe6SJojI/AAAAAAAAA-U/KOem15N3siE/s1600/Panel+de+control+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="90" src="http://1.bp.blogspot.com/-LMixfX-5lvc/TmEZe6SJojI/AAAAAAAAA-U/KOem15N3siE/s200/Panel+de+control+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://4.bp.blogspot.com/-8O2vJ5qzqs4/TmEZfhvAEWI/AAAAAAAAA-Y/-LKQlr2hw_k/s1600/Panel+de+control+3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="90" src="http://4.bp.blogspot.com/-8O2vJ5qzqs4/TmEZfhvAEWI/AAAAAAAAA-Y/-LKQlr2hw_k/s200/Panel+de+control+3.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-UG-FU4m3D-g/TmEZgmPjnCI/AAAAAAAAA-c/hWmWT_2pw0c/s1600/Prueba.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="90" src="http://1.bp.blogspot.com/-UG-FU4m3D-g/TmEZgmPjnCI/AAAAAAAAA-c/hWmWT_2pw0c/s200/Prueba.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;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-lK0madv1LjY/TmEZd1p1wTI/AAAAAAAAA-Q/AoS7baiiP7s/s1600/Console.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="90" src="http://2.bp.blogspot.com/-lK0madv1LjY/TmEZd1p1wTI/AAAAAAAAA-Q/AoS7baiiP7s/s200/Console.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Una vez que la tarea (o proyecto) se lanza en la opción Salida de consola podemos ver la salida de la tarea lanzada, muy util si queremos ver que ha sucedido en ella.&lt;br /&gt;
&lt;br /&gt;
Para instalar el plugin de git basta ir al apartado «Administrar Jenkins &amp;gt; Administrar Plugins &amp;gt; Todos los plugins» marcar el de git, pulsar el botón instalar al final de la página y reiniciar jenkins.&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/-S6ru2oUTd8I/TmEWTdvlybI/AAAAAAAAA94/2G8qBWtuwNE/s1600/Administrar.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="93" src="http://4.bp.blogspot.com/-S6ru2oUTd8I/TmEWTdvlybI/AAAAAAAAA94/2G8qBWtuwNE/s200/Administrar.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-yQRObCUQzP4/TmEWWsVmbQI/AAAAAAAAA-I/qCagt0XFUIw/s1600/Update+Center.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="24" src="http://3.bp.blogspot.com/-yQRObCUQzP4/TmEWWsVmbQI/AAAAAAAAA-I/qCagt0XFUIw/s200/Update+Center.png" style="cursor: move;" 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://1.bp.blogspot.com/-cquxY93bZsE/TmEWUBsVKWI/AAAAAAAAA98/lmWEMx55Pdk/s1600/Centro+de+actualizaciones.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="85" src="http://1.bp.blogspot.com/-cquxY93bZsE/TmEWUBsVKWI/AAAAAAAAA98/lmWEMx55Pdk/s200/Centro+de+actualizaciones.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
En la pantalla de Panel de control podemos obtener en laces a fuentes RSS de los proyectos fallidos, de los más recientes o de todos los trabajos para recibir notificaciones por esta vía.&lt;br /&gt;
&lt;br /&gt;
Usar una herramienta continua como jenkins además de las ventajas que ya de por si aporta con la integración continua es motivador para el equipo de desarrollo ya que en cierta forma le obliga a conseguir que la última ejecución de las tareas sea en todo momento correcto. Es una de esas herramientas imprescindibles a usar en cualquier proyecto.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;http://en.wikipedia.org/wiki/Continuous_integration&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://jenkins-ci.org/"&gt;http://jenkins-ci.org/&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/Meet+Jenkins"&gt;https://wiki.jenkins-ci.org/display/JENKINS/Meet+Jenkins&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/Use+Jenkins"&gt;https://wiki.jenkins-ci.org/display/JENKINS/Use+Jenkins&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/Extend+Jenkins"&gt;https://wiki.jenkins-ci.org/display/JENKINS/Extend+Jenkins&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/Containers"&gt;https://wiki.jenkins-ci.org/display/JENKINS/Containers&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-3947454537769489965?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/0sKXRCBCy3o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/3947454537769489965/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/09/integracion-continua-con-jenkins.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3947454537769489965?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/3947454537769489965?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/0sKXRCBCy3o/integracion-continua-con-jenkins.html" title="Integración continua con Jenkins" /><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/-VDA-zXScjY8/TmEXmNUTE4I/AAAAAAAAA-M/9Ox1D_VDmvQ/s72-c/jenkins.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/09/integracion-continua-con-jenkins.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0cDRXwyeyp7ImA9WhRUFUo.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-7823821227942592875</id><published>2011-08-19T19:21:00.000+02:00</published><updated>2012-01-26T10:44:34.293+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-26T10:44:34.293+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="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><title>Motivos para elegir el framework Apache Tapestry</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;
En el mundo del desarrollo web en el entorno Java hay muchos frameworks disponibles en los que elegir (JSP/Servlets, Struts, Grails, Wicket, Play, Seam, ... Tapestry y varios más), a veces la elección del framework más adecuado para el desarrollo del proyecto que tenemos por delante no suele ser una decisión sencilla y no solo intervienen decisiones técnicas sino también los conocimientos y predisposición de aprender que tenga el equipo que vaya a desarrollarlo. Lo que está claro es que si el proyecto es más o menos complejo o va a necesitar mantenimiento nadie se plantea desarrollarlo directamente con JSP y Servlets.
&lt;br /&gt;
&lt;br /&gt;
En la siguiente entrada voy a tratar de explicar algunas características y dar algunos motivos y razones por los que deberías tener en cuenta el framework Apache Tapestry.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Componentes&lt;/b&gt;&lt;br /&gt;
La característica estrella para mi de este framework es la alta productividad y la reutilización de código que se puede conseguir con los componentes que se desarrollan en el framework ya que una vez desarrollados pueden ser usados tantas veces como se quiera a lo largo de todo el proyecto sin tener que conocer los detalles de como y que necesitan para funcionar salvo por los parámetros que recibe. Esta abstracción incluye los estilos y el javascript y Tapestry se encargará de agregar e incluir únicamente los necesarios en la página según la definición de los mismos.&lt;br /&gt;
&lt;br /&gt;
Prácticamente en todos los proyectos hay necesidad de desarrollar una serie de funcionalidades o componentes comunes que son usados repetidas veces en el mismo o en diferentes proyectos. ¿Cuantas veces en un mismo proyecto has tenido que implementar la misma funcionalidad en páginas diferentes? ¿y cuantas veces te has tenido que pelear con sus parámetros que hay que recoger de la request en forma cruda (String) necesitando convertir su tipo correcto o transformarlo a las entidades de dominio? ¿y si tienes que hacer la misma funcionalidad en varios sitios de la misma aplicación, copias y pegas? ¿y si tienes que cambiar algo común lo tienes que hacer en todos los sitios? ¿cuanto código duplicado tienes? Está claro que tener código duplicado desperdigado en nuestra aplicación a la larga será un gran problema en el mantenimiento, pues bien, en con este framework el componente será solo uno y si tienes que hacer un cambio solo necesitarás modificarlo en un sitio. Solo tienes que pensar en cual es la funcionalidad que quieres reutilizar (en la misma o diferentes páginas) y meterla en un componente. El desarrollo orientado a componentes predispone para reutilizar código.&lt;br /&gt;
&lt;br /&gt;
En frameworks como Struts y Grails se trabaja en términos de URL y parámetros en Tapestry en términos de componentes y objetos. Tapestry se encarga de convertir los parmámetros de la URL al tipo adecuado y de dejarlo en una propiedad del componente de ese tipo. Tapestry puede transformar un String a un tipo de objeto en concreto (por ejemplo una clase de dominio) y viceversa, esto es necesario ya que todo lo que se envía al navegador del usuario ha de ser un String ¿quien realiza esa transformación del objeto a String? ¿y de String al objeto cuando se vuelvan a enviar los datos al servidor? Todo esto lo hace a través de una clase que implementa la interfaz &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Translator.html"&gt;Translator&lt;/a&gt;&amp;nbsp;o&amp;nbsp;&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ValueEncoder.html"&gt;ValueEncoder&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
También, cuando pasamos un parámetro a un componente es capaz de hacer las conversiones necesarias para transformar el tipo del parámetro que pasamos al tipo de parámetro del componente. En el gráfico de &lt;a href="http://tapestry.apache.org/typecoercer-service.html"&gt;la documentación de conversiones&lt;/a&gt; se pueden ver las que hace se hacer por defecto aunque podríamos implementar una nueva si tuviesemos necesidad.&lt;br /&gt;
&lt;br /&gt;
Además los componentes pueden ser empaquetados en una librería (un archivo .jar) y ser reutilizados en varios proyectos cosa que hará que el tiempo de desarrollo de los siguientes proyectos se reduzca bastante si las necesidades son parecidas. También a medida que vayamos tienendo una librería de componentes seremos más productivos en el mismo proyecto si es de larga duración o tiene mucho mantenimiento.&lt;br /&gt;
&lt;br /&gt;
Pero tampoco hay que desarrollar los componentes desde cero, Tapestry ya proporciona una &lt;a href="http://tapestry.apache.org/current/tapestry-core/ref/"&gt;amplia colección&lt;/a&gt; de ellos listos para usar directamente o agregándolos en los nuevos que creemos y hay varias librerías de ellos desarrolladas en otros proyectos. Hay componentes para tratar campos de formularios, que permiten evaluar una condición, para recorrer una colección de elementos, para crear enlaces, para mostrar errores... también hay otros tan complejos como un grid para mostrar datos en una tabla paginada, para editar un bean o para cargar contenido en las páginas a través de AJAX y algunos otros para otras tareas.&lt;br /&gt;
&lt;br /&gt;
En el componente grid que proporciona unicamente tendrás que implementar el método que será el que recupere los objetos de la base de datos ya que del resto se  encargará el componente llamando a nuestro método de búsqueda con los parámetros necesarios para realizarla, como la página a mostrar. Pero hacer un nuevo componente no es nada complejo, cada componente está formado de un controlador (que es una simple clase Java POJO) y normalmente una platilla que es XHTML válido.&lt;br /&gt;
&lt;br /&gt;
Otra características importante es que los componentes pueden lanzar eventos que pueden ser procesados por aquellos que los contienen, pudiendo de esta forma realizar diferentes acciones y comportamientos dependiendo del componente en el que está contenido. Esto añade más posibilidades de reutilización de los componentes.&lt;br /&gt;
&lt;br /&gt;
Si desarrollamos muchos proyectos pequeños y que no tienen mucho mantenimiento podemos reutilizar los componentes que desarrollamos en un proyecto en otro, si desarrollamos uno, dos o pocos proyectos pero que tienen un gran mantenimiento podemos reutilizarlos igualmente en las funcionalidades comunes. En ambos casos de tipos de proyectos nos beneficiaremos enormemente de los componentes.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Modular y contenedor IOC integrado&lt;/b&gt;&lt;br /&gt;
Práctiamente cada funcionalidad de Tapestry puede ser modificada o extendida a través de su contenedor de inversión de control, solo hace falta hacer la contribución adecuada en el contenedor. Además, esas contribuciones no se hace en un XML sino que se hace a través de código Java con las ventajas que ello conlleva como la asistencia del compilador y la refactorización del IDE. El contenedor IOC implementa usa y pone a nuestra disposición varios de los patrones más usados como Chain of Command, Strategy Builder, Chain Builder, Shadow Builder, Pipelines, etc... Finalmente no impone un determinado framework de persistencia, de testing, de seguridad u otra cosa por lo que tendremos libertad de elegir la que más adecuado nos parezca en cada ámbito a resolver. En un mundo tan cambiante como la tecnología cuando surja esa otra nueva y brillante forma de hacer las cosas no nos veremos encadenados a una en concreto porque Tapestry nos lo exija y eso tarde o temprano pasará. ¿Quizá dentro de poco pase con &lt;a href="http://www.hibernate.org/"&gt;hibernate&lt;/a&gt; y &lt;a href="http://code.google.com/p/activejdbc/"&gt;activejdbc&lt;/a&gt;? ¿Te gustaría estar encadenado a un framework que te imponga un modelo o framework de persistencia en concreto?&lt;br /&gt;
&lt;br /&gt;
Hay plugins para las herramentas más habituales como Hibernate, String, Quartz, JFreeChart, Lucene, ... y si no existe siempre se puede hacer una integración definiendo un nuevo servicio. Como he dicho prácticamente cualquier apartado puede ser extendido o adaptado a nuestras necesidades. ¿Necesitas un validador o un objeto que convieta de un tipo a otro? No hay problema, implementa en un objeto la interfaz adecuada, configuralo en la aplicación y él se encarga de usarlo cuando sea necesario.&lt;br /&gt;
&lt;br /&gt;
Tapestry no es un framework «full stack» pero eso... ¿es un defecto o una ventaja? Dependiendo del tipo de proyecto, nuestras preferencias o necesidades puede ser una cosa u otra.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Exception reporting&lt;/b&gt;&lt;br /&gt;
Otra buena característica de Tapestry es que cuando algo va mal no solo nos da la traza de la excepción que se ha producido en la consola del servidor sino que nos muestra una página con toda la información de la aplicación y de la petición realizada y que ha fallado, con lo que descubrir la causa de la excepción nos será más sencillo. ¿Te has exasperado alguna vez porque la línea que se indica en un error no se corresponde con la realidad? En el informe verás la linea &lt;strong&gt;exacta&lt;/strong&gt; que ha producido la excepción y un extracto del código fuente de los componentes y los usos de unos a otros hasta llegar a la excepción, además en la traza el informe te marcará en otro color las clases que se han usado de tu aplicación. Por supuesto está pagina puede ser personalizada en un entorno de producción. En Tapestry 5.3 se ha incorporado un informe incluso para las peticiones AJAX y el autor del framework ha publicado un &lt;a href="http://tapestryjava.blogspot.com/2011/08/tapestry-53-ajax-exception-reporting.html"&gt;screencast en el que se pueden ver los informes de errores&lt;/a&gt;&amp;nbsp;en acción.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Productividad&lt;/b&gt;&lt;br /&gt;
En caso de hacer modificaciones en las clases de los componentes, páginas y servicios o a las plantillas de los mismos los cambios se hacen efectivos inmediatamente sin tener que realizar un redespliegue de la aplicación aunque en algunos casos no nos quede más remedio que hacerlo. Aún en ese caso la aplicación tarda poco en arrancar comparado con otros frameworks. Si desarrollamos en Java podemos aprovecharnos del soporte de refactor que ofrecen los IDE. La productividad se consigue reutilizando componentes ya desarrollados y probados.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Rendimiento y escalabilidad&lt;/b&gt;&lt;br /&gt;
En las últimas versiones se han incorporado características para aumentar el rendimiento y su escalabilidad como la eliminación del pool de páginas, ahora solo hay una instancia para todas las peticiones de la aplicación, la eliminación de espacios en blanco innecesarios lo que reduce el tamaño del contenido a devolver, la compresión del contenido devuelto para reducirlo aún más y la agregación de javascripts y hojas de estilo para reducir el número de peticiones a realizar al servidor. Con estas características se consigue que la página carge más rápido en el cliente y disminuya la carga de trabajo en el servidor al realizarse menos peticiones.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Soporte localización&lt;/b&gt;&lt;br /&gt;
En otros frameworks es habitual que solo exista un bundle con las traducciones de toda la aplicación. Tapestry permite una granularidad mayor, los componentes pueden tener su propio bundle de propeties con las traducciones específicas de ese componente, además los componentes y páginas pueden tener una plantilla diferente según el locale (y a partir de la versión 5.3 podriamos tener una &lt;a href="http://blog.tapestry5.de/index.php/2011/06/24/template-skinning/"&gt;plantilla diferente si el cliente es un dispositivo movil&lt;/a&gt; u otras características que decidamos) e incluso las imágenes con textos son recursos que podrían localizarse eligiendo la adecuada según el lenguaje de la aplicación.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Validaciones&lt;/b&gt;&lt;br /&gt;
La validación de los datos de entrada de un usuario de la aplicación es una de las cosas importantes que como desarrolladores tenemos que tratar. La validaciones son muy simples de aplicar ya que se hace de forma declarativa dejando a Tapestry todo el trabajo de comprobarlas en el servidor. Además es capaz de presentar los errores al usuario en caso de que alguna haya producido algún error. Soporta la especificación JSR-303.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Políglota&lt;/b&gt;&lt;br /&gt;
¿Te gustan otros lenguajes que han aparecido recientemente y que se pueden ejecutar sobre la Java VM como Groovy o Scala? Pues con él puedes desarrollar tus componentes, páginas y servicios con el lenguaje que prefieras ya sea Java, Groovy, Scala o cualquier otro que se pueda ejecutar sobre la máquina virtual. Puedes ver un ejemplo en otra entrada que he escrito &lt;a href="http://elblogdepicodev.blogspot.com/2011/07/usar-apache-tapestry-5-con-groovy-u.html"&gt;Usar Apache Tapestry 5 con Groovy (u otros lenguajes de la JVM)&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Otras características&lt;/b&gt;&lt;br /&gt;
Tapestry implementa el patrón «&lt;b&gt;redirect after post&lt;/b&gt;» que resuelve algunos problemas inherentes a las aplicaciones web o los botones atrás y adelante de los navegadores. En un artículo de theserverside, &lt;a href="http://www.theserverside.com/news/1365146/Redirect-After-Post"&gt;Redirect After Post&lt;/a&gt;, tienes una explicación más detallada de este patrón.&lt;br /&gt;
&lt;br /&gt;
Además de las anteriores, y en mi opinión personal, otra es que con él realmente disfruto programando y quedo satisfecho de como queda el trabajo que realizo. ¿Se podría decir algo mejor de un framework de forma resumida?&lt;br /&gt;
&lt;br /&gt;
Por supuesto no es perfecto y estas&amp;nbsp;&amp;nbsp;son&amp;nbsp;solo unas pocas de sus características pero creo que en los aspectos importantes como la reutilización de código en base a los componentes, su modularidad, adaptabilidad y gran rendimiento, productividad y su desarrollo sencillo una vez aprendidos sus conceptos básicos considero que gana por mucha diferencia respecto a otros frameworks. Una de sus deficiencias respecto a otros frameworks (o no) como Grails y otros parecidos en otros lenguajes (PHP, Ruby) es que no proporciona un entorno tan integrado («full stack») como la posibilidad de ejecutar los teses con grails test-app o de ejecutar la aplicación grails run-app, pero esto también tiene la ventaja de que no se nos impone uno y podemos usar el que queramos y tenemos mayor control sobre lo que hacemos.&lt;br /&gt;
&lt;br /&gt;
La versión actual de Tapestry es la 5.2.6 estando al versión 5.3 en estos momentos en proceso de entrar en estado beta. Los cambios que incorporará 5.3 pueden consultarse en las&amp;nbsp;&lt;a href="http://tapestry.apache.org/release-notes-53.html"&gt;notas de publicación de la versión&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Si tienes curiosidad de ver algo de código de una aplicación puedes ver estas entradas: &lt;a href="http://elblogdepicodev.blogspot.com/2010/05/hola-mundo-con-apache-tapestry-5.html"&gt;Hola Mundo con Tapestry 5&lt;/a&gt;, &lt;a href="http://elblogdepicodev.blogspot.com/2011/01/componente-lista-para-tapestry-5.html"&gt;Componentes lista paginada con Tapestry&lt;/a&gt; y si quieres ver una aplicación funcionando puedes acceder a este &lt;a href="http://hello-world-tapestry5-gae.appspot.com/"&gt;Hola Mundo con Tapestry 5&lt;/a&gt; que se está ejecutando en el Google App Engine. También en la aplicación&amp;nbsp;&lt;a href="http://draft.blogger.com/blogger.g?blogID=2347950092468997900"&gt;JumpStart&lt;/a&gt; tienes un montón de ejemplos&amp;nbsp;&amp;nbsp;interesantes&amp;nbsp;con el código fuente sobre diversas características de él, seguro te sorprende lo simple y «limpio» que es el código para las cosas que se hacen en esos ejemplos.&lt;br /&gt;
&lt;br /&gt;
En la entrada &lt;a href="http://elblogdepicodev.blogspot.com/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Tapestry&lt;/a&gt; puedes ver la documentación disponible que he ido recopilando por la red.&lt;br /&gt;
&lt;br /&gt;
Espero que estos motivos os animen a echarle un vistazo, con que sólo uno lo hiciera ya me sentiría recompensado por haber escrito la entrada. Si es así pueden estar atentos a las entradas que escriba en mi blog ya que pretendo seguir escribiendo sobre Tapestry en él :).&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://mail-archives.apache.org/mod_mbox/tapestry-users/201201.mbox/%3C4F210FCA.3020801@ictjob.be%3E"&gt;http://mail-archives.apache.org/mod_mbox/tapestry-users/201201.mbox/%3C4F210FCA.3020801@ictjob.be%3E&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-7823821227942592875?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/2Teol7taABc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/7823821227942592875/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/08/motivos-para-elegir-apache-tapestry.html#comment-form" title="2 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7823821227942592875?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/7823821227942592875?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/2Teol7taABc/motivos-para-elegir-apache-tapestry.html" title="Motivos para elegir el framework 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>2</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/08/motivos-para-elegir-apache-tapestry.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUAQ3w6eSp7ImA9WhdQF0k.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-6950148231198027600</id><published>2011-08-11T22:54:00.005+02:00</published><updated>2011-08-19T10:24:02.211+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-19T10:24:02.211+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="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><title>Seleccionar el lenguaje (locale) según el dominio en Apache Tapestry</title><content type="html">&lt;div class="separator" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"&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;a href="http://tapestry.apache.org/"&gt;Apache Tapestry&lt;/a&gt; por defecto determina el lenguaje (locale) de la aplicación por los headers que envía el navegador del usuario con sus preferencias. Si tenemos un sitio internacional con varios dominios y soportando varios lenguajes para cada país o dominio quizá nos interese más determinar el idioma del usuario por el dominio al que accede. Una de las cosas excelentes de Tapestry es que es extremadamente modular y extensible y &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/package-summary.html"&gt;cualquier cosa que es definida como un servicio&lt;/a&gt; puede ser adaptada a nuestras necesidades a través del contenedor de dependencias (IOC).&lt;br /&gt;
&lt;br /&gt;
Para conseguir lo que queremos el servicio que debemos redefinir es &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/LocalizationSetter.html"&gt;org.apache.tapestry5.services.LocalizationSetter&lt;/a&gt;&amp;nbsp;que es el responsable de establecer el locale según las preferencias enviadas por el usuario en la petición. Por tanto debemos desarrollar una clase con la lógica que necesitamos y que implemente esa interfaz. Pero no hace falta que la desarrollemos desde cero, podemos partir de la que usa Tapestry internamente &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/internal/services/LocalizationSetterImpl.html"&gt;org.apache.tapestry5.internal.services.LocalizationSetterImpl&lt;/a&gt; (el software libre/código abierto es genial ¿verdad?).&lt;br /&gt;
&lt;br /&gt;
La parte interesante e importante que debemos redefinir es la siguiente:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;	/* AppLocalizationSetter.java */
	...
	private final Request request;
                private final PersistentLocale persistentLocale;
        	private final ThreadLocale threadLocale;

                private Map&amp;amp;lt;String, String&amp;amp;gt; defaultLocales;
                ...
                public AppLocalizationSetter(Request request, PersistentLocale persistentLocale, ThreadLocale threadLocale, @Symbol(SymbolConstants.SUPPORTED_LOCALES) String localeNames, ...) {
                        ...
                        this.defaultLocales = new HashMap&amp;amp;lt;String, String&amp;amp;gt;();
                }
	...
	public boolean setLocaleFromLocaleName(String localeName) {
		boolean supported = isSupportedLocaleName(localeName);
		
		Locale supportedLocale = findClosestSupportedLocale(localeName);
		if (!localeName.trim().equals("")) {
			persistentLocale.set(supportedLocale);		
		}

		threadLocale.setLocale(supportedLocale);

		return supported;
        }
        ...
	private Locale findClosestSupportedLocale(String desiredLocale) {
		String localeName = desiredLocale;

		while (true) {
			if (isSupportedLocaleName(localeName))
				return toLocale(localeName);

			localeName = stripTerm(localeName);

			if (localeName.length() == 0)
				break;
		}
               return getDefaultLocale(desiredLocale);
	}

	private String getDomainLocale() {
		String dominio = request.getServerName();
		if (defaultLocales.get(dominio) != null) {
			return defaultLocales.get(dominio);
		}
		// Buscar el idioma según el dominio
                // ¿en una constante?
                // ¿quizá en base de datos?
		String locale = ...;
		defaultLocales.put(dominio, locale);
                return locale;
	}

	private Locale getDefaultLocale(String desiredLocale) {
                String domainLocale = getDomainLocale();
        
                if (domainLocale.equals(desiredLocale)) {
                        // El locale del dominio no está soportado
            	        return supportedLocales.get(0);
                }
        
                Locale defaultLocale = findClosestSupportedLocale(domainLocale);
        
                return defaultLocale;
	}
	...

	/* AppModule.java */
	...
	public static void bind(ServiceBinder binder) {
		binder.bind(LocalizationSetter.class, AppLocalizationSetter.class).withId("AppLocalizationSetter");
	}
	
	public static void contributeServiceOverride(MappedConfiguration&lt;class, object=""&gt; configuration, @Local LocalizationSetter override) {
		configuration.add(LocalizationSetter.class, override);
	}
	...

&lt;/class,&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Lo importante a modificar es la forma de encontrar un locale soportado dado un localeName e implementar el método getDomainLocale() que tendrá nuestra lógica, obteniendo el Locale a partir de dominio por el que se ha accedido a la aplicación («request.getServerName()»). La forma de hacer la correspondencia entre el dominio y Locale la podemos hacer de la mejor forma que se nos ocurra de forma simple con un mapa con los dominios como claves y los locales como valor o accediendo a una base de datos que tenga la correspondencia.&lt;br /&gt;
&lt;br /&gt;
Tapestry codifica en las URLs el idioma del usuario para hacerlo persistente, esto se hace con &lt;a href="http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapestry5/services/PersistentLocale.html"&gt;persistentLocale.set(locale)&lt;/a&gt;. Si el locale que se intenta establecer está soportado por la aplicación se hacer persistente para posteriores peticiones y se hace efectivo para la petición actual. Si el locale que se intenta establecer no está directamente soportado se busca uno más general soportado, esto es lo que hace el método findClosestSupportedLocale. Por ejemplo, si se intenta usar el locale «en_US» y nuestra aplicación no lo soporta pero si soporta «en» se usa este último. Finalmente, si no encuentra ninguno devuelve el del dominio con la lógica que hemos definido y si tampoco es uno de los soportados de entre los de la lista del símbolo&amp;nbsp;SymbolConstants.SUPPORTED_LOCALES finalmente se usa el del defecto de la aplicación (el primero de esa lista).&lt;br /&gt;
&lt;br /&gt;
Deberemos hacer las contribuciones de nuestra aplicación en AppModule.java que sobreescriben el sevicio usado por defecto en Tapestry por el sevicio con nuestra lógica.&lt;br /&gt;
&lt;br /&gt;
Tapestry tiene un gran diseño y es muy flexible y adaptable, este es un ejemplo, redefiinimos el comportamiento con una nueva lógica que está localizada en una clase y puede ser fácilmente redefinida por otra en el momento que tengamos necesidad sin tener que cambiar nada de código en el resto de la aplicación salvo en el AppModule.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-6950148231198027600?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/ZC79Jv2MsDw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/6950148231198027600/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/08/seleccionar-el-lenguaje-locale-segun-el.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/6950148231198027600?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/6950148231198027600?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/ZC79Jv2MsDw/seleccionar-el-lenguaje-locale-segun-el.html" title="Seleccionar el lenguaje (locale) según el dominio 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/2011/08/seleccionar-el-lenguaje-locale-segun-el.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D04NSXo-cSp7ImA9WhdRFUs.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-8907477800078620938</id><published>2011-08-05T19:39:00.000+02:00</published><updated>2011-08-05T19:39:58.459+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-05T19:39:58.459+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><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="programación" /><title>Internacionalización a lenguajes con diferentes formas plurales en Java</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;
&lt;br /&gt;
&lt;img alt="Java" border="0" height="200" src="http://2.bp.blogspot.com/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s200/java.png" title="Java" width="107" /&gt;&lt;span id="goog_139179478"&gt;&lt;/span&gt;&lt;/div&gt;
Si desarrollamos una aplicación para diferentes lenguajes y por tanto necesitamos internacionalizarla es probable que tengamos que tener en cuenta el número de diferentes formas plurales que tengan esos lenguajes. El español tiene dos formas plurales o tipos de número (singular/1, plural/1+) pero &lt;a href="http://es.wikipedia.org/wiki/N%C3%BAmero_gramatical"&gt;otros lenguajes tienen más de dos formas plurales&lt;/a&gt; (nular/0, dual/2, trial/3, paucal/grupo reducido). Java da soporte a esta funcionalidad a través de la clase &lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/ChoiceFormat.html"&gt;ChioceFormat&lt;/a&gt; aunque nos requerirá un poco más de trabajo formatear cada mensaje.&lt;br /&gt;
&lt;br /&gt;
A través de la clase ChoiceFormat elegimos la cadena con la forma plural correcta, posteriormente tal vez debamos hacer sustituciones en el mensaje con un &lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/MessageFormat.html"&gt;MessageFormat&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
En el &lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/ChoiceFormat.html"&gt;javadoc de ChoiceFormat&lt;/a&gt; viene explicado como construir un patrón para posteriormente con la cantidad de elementos seleccionar la forma plural correcta.&lt;br /&gt;
&lt;br /&gt;
Estos dos pasos quedarían así:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;import java.text.ChoiceFormat;
import java.text.MessageFormat;

public class Test {
    public static void main(String[] args) {
	// Construir el ChoiceFormat
        ChoiceFormat cf = new ChoiceFormat("0#Hay {0} elementos.|1#Hay {0} elemento.|1&amp;lt;Hay {0} elementos.");
        String m = null;
        double n = 0;

        // Obtener la forma plural
        n = -3;
        m = cf.format(n)

        // Formatear
        System.out.println(MessageFormat.format(m, n));

        n = 0.5;
        m = cf.format(n)
        System.out.println(MessageFormat.format(m, n));
        
        n = 1;
        m = cf.format(n)
        System.out.println(MessageFormat.format(m, n));

        n = 2.5;
        m = cf.format(n)
        System.out.println(MessageFormat.format(m, n));

        n = 3;
        m = cf.format(n)
        System.out.println(MessageFormat.format(m, n));
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
El resultado de este programa es:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;Hay -3 elementos.
Hay 0,5 elementos.
Hay 1 elemento.
Hay 2,5 elementos.
Hay 3 elementos.&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Si el String que se le pasa al ChoiceFormat viniese de un archivo .properties con los mensajes de cada idioma podríamos soportar varias formas plurales cambiando los límites en el mensaje localizado. En español tendríamos un literal en un properties tal que:  &lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;elementos=0#Hay cero elementos.|1#Hay un elemento.|1&amp;lt;Hay algo más de un elemento.&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Pero en otro idioma podríamos tener esto según las diferentes formas plurales de ese idioma: &lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;elementos=0#Hay menos de un elemento.|1#Hay un elemento.|1&amp;lt;Hay algo más de un elemento.|2#Hay dos elementos||2#Hay algo más de dos elementos.|3#Hay tres o más elementos.&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;Hay menos de un elemento.
Hay menos de un elemento.
Hay un elemento.
Hay algo más de dos elementos.
Hay tres o más elementos.&lt;/pre&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/-Y1TqPzot36A/TjwpIJDs5iI/AAAAAAAAA90/QDXeeTU82uo/s1600/ChoiceFormat.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-Y1TqPzot36A/TjwpIJDs5iI/AAAAAAAAA90/QDXeeTU82uo/s200/ChoiceFormat.png" width="182" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/ChoiceFormat.html"&gt;http://download.oracle.com/javase/7/docs/api/java/text/ChoiceFormat.html&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://download.oracle.com/javase/7/docs/api/java/text/MessageFormat.html"&gt;http://download.oracle.com/javase/7/docs/api/java/text/MessageFormat.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-8907477800078620938?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/sCkgUvYbVro" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/8907477800078620938/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/08/internacionalizacion-lenguajes-con.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8907477800078620938?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/8907477800078620938?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/sCkgUvYbVro/internacionalizacion-lenguajes-con.html" title="Internacionalización a lenguajes con diferentes formas plurales en Java" /><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/-hC2-QuC33AU/TBeztET3lAI/AAAAAAAAAZ8/EzZnNZw5do0/s72-c/java.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/08/internacionalizacion-lenguajes-con.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIMQ38-eSp7ImA9WhRUEUs.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-1893061755351317672</id><published>2011-07-24T11:46:00.007+02:00</published><updated>2012-01-21T17:16:22.151+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-21T17:16:22.151+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software privativo" /><category scheme="http://www.blogger.com/atom/ns#" term="tecnología" /><category scheme="http://www.blogger.com/atom/ns#" term="otros" /><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="Apple" /><category scheme="http://www.blogger.com/atom/ns#" term="Microsoft" /><category scheme="http://www.blogger.com/atom/ns#" term="GNU/Linux" /><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu" /><title>Un día en la Euskal Encounter 19</title><content type="html">&lt;div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img alt="Euskal encounter 19" border="0" src="http://1.bp.blogspot.com/-XcMwHk7AABo/TiqxFNgHm7I/AAAAAAAAA50/Ot6_eSvAkEA/s1600/euskal-encounter.png" title="Euskal encounter 19" /&gt;&lt;/div&gt;En estos días se está celebrando, del 22 al 25 de julio, la&amp;nbsp;&lt;a href="http://www.euskal.org/"&gt;Euskal Encounter&lt;/a&gt;, uno de los enventos informáticos más importantes de Euskadi, del estado de España y según algunos uno de los mejores de parte de Europa. Así desde hace ya 19 ediciones, con en estos días exactamente 4096 puestos para los asistentes que disponen de una conexión de 20 Gb por segundo ofrecida por la mano de Euskaltel y HP.&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/-l99Z53pT6aQ/Tiq05lW8FXI/AAAAAAAAA6E/Olu9LltUxY4/s1600/DSCN0130.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-l99Z53pT6aQ/Tiq05lW8FXI/AAAAAAAAA6E/Olu9LltUxY4/s200/DSCN0130.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-SKInSG7mKf8/Tiq6WvHVMhI/AAAAAAAAA8c/c0fNIcXlILE/s1600/DSCN0100.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-SKInSG7mKf8/Tiq6WvHVMhI/AAAAAAAAA8c/c0fNIcXlILE/s200/DSCN0100.JPG" 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;A pesar de que se organiza en Barakaldo, al alcance de metro de ciudad en la que vivo y escasos minutos, hasta este año no había asistido a ninguna. Tenía muchas ganas de ir y vivir en primera persona lo que ahí acontecía al menos por un día, este año no lo quería dejar pasar y así ha sido. Como repaso de lo que ha dado el día (y parte de la noche) os escribo esta entrada.&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/-GugFBA9GHdM/Tiq0nffIY9I/AAAAAAAAA58/vdNrh_fUG0I/s1600/DSCN0080.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-GugFBA9GHdM/Tiq0nffIY9I/AAAAAAAAA58/vdNrh_fUG0I/s200/DSCN0080.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-qaQZImNmiCM/Tiq0myG8KLI/AAAAAAAAA54/Gigm_hRQ2OU/s1600/DSCN0079.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-qaQZImNmiCM/Tiq0myG8KLI/AAAAAAAAA54/Gigm_hRQ2OU/s200/DSCN0079.JPG" 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://2.bp.blogspot.com/-sz30NH5qnAw/Tiq0nhgu4FI/AAAAAAAAA6A/Os5Gx9LEW9E/s1600/DSCN0081.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-sz30NH5qnAw/Tiq0nhgu4FI/AAAAAAAAA6A/Os5Gx9LEW9E/s200/DSCN0081.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-Shrd_4D_ZXQ/TivefSEPtCI/AAAAAAAAA9M/jeJdqxx7a9s/s1600/DSCN0175.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-Shrd_4D_ZXQ/TivefSEPtCI/AAAAAAAAA9M/jeJdqxx7a9s/s200/DSCN0175.JPG" 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://1.bp.blogspot.com/-XUN6Ljvez88/Tivep1ukhwI/AAAAAAAAA9Q/vC4X4urqkSY/s1600/DSCN0176.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-XUN6Ljvez88/Tivep1ukhwI/AAAAAAAAA9Q/vC4X4urqkSY/s200/DSCN0176.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-B09i7PJ9AfY/Tivey97B0TI/AAAAAAAAA9U/3sIwQkNmm7k/s1600/DSCN0177.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-B09i7PJ9AfY/Tivey97B0TI/AAAAAAAAA9U/3sIwQkNmm7k/s200/DSCN0177.JPG" 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;div style="text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://2.gvt0.com/vi/lPd6UVxfZkY/0.jpg" height="384" width="512"&gt;&lt;param name="movie" value="http://www.youtube.com/v/lPd6UVxfZkY&amp;fs=1&amp;source=uds" /&gt;  &lt;param name="bgcolor" value="#FFFFFF" /&gt;  &lt;embed width="512" height="384"  src="http://www.youtube.com/v/lPd6UVxfZkY&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;El asistir me pedía tener todo preparado para que no me faltara nada, cámara de fotos, camiseta con el logo de Arch Linux para hacer promoción ;), disco duro con el mayor espacio posible por si en algún momento descargaba algo (aunque con la entrada de un día no da para disponer de conexión por suerte encontré un sitio libre) más que nada quería probar que daban de sí esos 20 Gb para todos a cada usuario, descargando con torrent se alzanzaban picos de 1 MiB/s, y también instalé el juego &lt;a href="http://www.wesnoth.org/"&gt;Battle for Wesnoth&lt;/a&gt;&amp;nbsp;para probarlo por si tenía algún momento desocupado.&lt;/div&gt;&lt;br /&gt;
He llegado a las 10:15 momentos poco después de que se abriera el plazo para conseguir la acreditación por un día. Nada más llegar mucha gente todavía descansando, se veía muchas pantallas sin encender y muchos sitios libres, a partir de la 13:00 ya se empezaba a notar más movimiento aunque aquí el horario es muy variable según cada persona. Mucho PC, alguno con Ubuntu y también bastante Mac los cuales son fácilmente reconocibles por su logotipo de manzana. Pensaba que iba a ver más portátiles pero muchos eran ordenadores con sus buenas pantallas de 21 y 27 pulgadas con lo que debe costar cargarlos a las espaldas y algunos mods muy bien hechos. Lo primero era hacer una ronda por todo el recinto. Había principalmente gente joven de 17 a 30 años pero también había gente ya entrada en la cuerentena y cincuentena. Y sí, también se veían chicas aunque en bastante menor proporción.&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/-TUCMUBKgMxg/Tiq1ItwgD-I/AAAAAAAAA6I/thcmEGDlvag/s1600/DSCN0111.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-TUCMUBKgMxg/Tiq1ItwgD-I/AAAAAAAAA6I/thcmEGDlvag/s200/DSCN0111.JPG" width="150" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-zRP0tfStUvI/Tiq1Qw9xdMI/AAAAAAAAA6M/5-OEUCQsuGw/s1600/DSCN0142.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-zRP0tfStUvI/Tiq1Qw9xdMI/AAAAAAAAA6M/5-OEUCQsuGw/s200/DSCN0142.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Están las zonas del gamegune, donde los pros de los juegos compiten, la zona de stands donde se puede ver una exposición con objetos de películas míticas con la máscara de Darth Vader y una maqueta de la estrella de la muerte de La guerra de las galaxias, el condensador de fluzo de regreso al futuro, las garras de Pesadilla en elm street, la ropa del Scream, la espada de conan, los trajes de dune, un pedrusco de criptonita, un billete de gotham y objetos de otras películas como Terminator. Un lugar de culto para aquellos que les gustan estas películas. Hay una zona con consolas XBOX con diferentes juegos, no podía dejar pasar la oportunidad de jugar un derby&amp;nbsp;entre el Athletic y la Ral Sociedad&amp;nbsp;ahora que a primero hora de la mañana no había nadie ni tampoco campeonatos, lamentablemente mi poca práctica desde hace mucho tiempo en juegos me hizo perderlo, decargué mi furia repartiendo golpes con un Street Figther :). Por supuesto después de tantas horas y días delante de una pantalla no podía faltar la zona dormitorio, era como una zona de setas formada por tiendas de campaña, se veían cosas menos elaboradas como colchonetas y mantas pero seguramente para descansar unas horas suficiente. La zona de stands estaba representada por algunas compañías de hardware y modding, la tienda de la Free Software Foundation donde se podían adquirir camisetas, un retromuseo donde se podían ver varios objetos ya obsoletos y consolas antiguas funcionando con juegos y la zona ExpoSyFy donde estaban los objetos comentados de películas. También había un par de escenarios y varias pantallas gigantes.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Gamegune&lt;/b&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-6TTLczgnkR8/Tiq2O8hVHBI/AAAAAAAAA7M/HxK6nt_MVaU/s1600/DSCN0090.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-6TTLczgnkR8/Tiq2O8hVHBI/AAAAAAAAA7M/HxK6nt_MVaU/s200/DSCN0090.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-lqNWg-y5YGk/Tiq2PvooD6I/AAAAAAAAA7Q/JdYU8fm6zxY/s1600/DSCN0092.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-lqNWg-y5YGk/Tiq2PvooD6I/AAAAAAAAA7Q/JdYU8fm6zxY/s200/DSCN0092.JPG" 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://1.bp.blogspot.com/-g2P4eaCYTN8/Tiq2QIVM8aI/AAAAAAAAA7U/El0dBCYtmg8/s1600/DSCN0093.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-g2P4eaCYTN8/Tiq2QIVM8aI/AAAAAAAAA7U/El0dBCYtmg8/s200/DSCN0093.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-Eah6d8u-aSg/Tiq2QsNadUI/AAAAAAAAA7Y/ULRE6C1_XCs/s1600/DSCN0094.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-Eah6d8u-aSg/Tiq2QsNadUI/AAAAAAAAA7Y/ULRE6C1_XCs/s200/DSCN0094.JPG" 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/-vBMpuiV9RYA/Tiq2RBARV0I/AAAAAAAAA7c/t8Om8rPfum8/s1600/DSCN0095.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-vBMpuiV9RYA/Tiq2RBARV0I/AAAAAAAAA7c/t8Om8rPfum8/s200/DSCN0095.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-x0puNXGbicI/Tiq2RqgLGiI/AAAAAAAAA7g/r6Aniy3T1Is/s1600/DSCN0096.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-x0puNXGbicI/Tiq2RqgLGiI/AAAAAAAAA7g/r6Aniy3T1Is/s200/DSCN0096.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;Stands, zona talleres y conferencias&lt;/b&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-qtJz6MRQyR8/Tiq2u1uRJ2I/AAAAAAAAA7k/l7Fb_n05MBQ/s1600/DSCN0108.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-qtJz6MRQyR8/Tiq2u1uRJ2I/AAAAAAAAA7k/l7Fb_n05MBQ/s200/DSCN0108.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-hcU9GRJRfS8/Tiq2vBF4TpI/AAAAAAAAA7o/lxCs2-Jy6r0/s1600/DSCN0109.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-hcU9GRJRfS8/Tiq2vBF4TpI/AAAAAAAAA7o/lxCs2-Jy6r0/s200/DSCN0109.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&amp;nbsp;&lt;a href="http://2.bp.blogspot.com/-tGw-8Uqo1Gw/Tiq2vkLrcQI/AAAAAAAAA7s/W3SjYkpbNJA/s1600/DSCN0112.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-tGw-8Uqo1Gw/Tiq2vkLrcQI/AAAAAAAAA7s/W3SjYkpbNJA/s200/DSCN0112.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Zona dormitorio&lt;/b&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-LfX_cmfgCZc/Tiq2OUdFeZI/AAAAAAAAA7I/Y1Zt_NKjej0/s1600/DSCN0089.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-LfX_cmfgCZc/Tiq2OUdFeZI/AAAAAAAAA7I/Y1Zt_NKjej0/s200/DSCN0089.JPG" width="150" /&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://2.bp.blogspot.com/-I7PPpoVpfsY/Tiq2E8EBJKI/AAAAAAAAA7A/lkh4Of0a4ig/s1600/DSCN0086.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-I7PPpoVpfsY/Tiq2E8EBJKI/AAAAAAAAA7A/lkh4Of0a4ig/s200/DSCN0086.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-H4wh6pCMa9I/Tiq2FXqg4MI/AAAAAAAAA7E/NEJIdOcj3is/s1600/DSCN0087.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-H4wh6pCMa9I/Tiq2FXqg4MI/AAAAAAAAA7E/NEJIdOcj3is/s200/DSCN0087.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;ExpoSyFy&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-R9hImSEUlwM/TirVWimBx6I/AAAAAAAAA8s/nS_YCAI2-xU/s1600/DSCN0114.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-R9hImSEUlwM/TirVWimBx6I/AAAAAAAAA8s/nS_YCAI2-xU/s200/DSCN0114.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-aDBQERlf704/Tiq1kjBG3tI/AAAAAAAAA6Q/lkx3Dx_uxOM/s1600/DSCN0115.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-aDBQERlf704/Tiq1kjBG3tI/AAAAAAAAA6Q/lkx3Dx_uxOM/s200/DSCN0115.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-SE2n0lbAb1w/Tiq1lBBH1uI/AAAAAAAAA6U/8S-owMwquMU/s1600/DSCN0116.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-SE2n0lbAb1w/Tiq1lBBH1uI/AAAAAAAAA6U/8S-owMwquMU/s200/DSCN0116.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&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/-F3b7V-iWlOc/Tiq1lUukCcI/AAAAAAAAA6Y/K4Y4Wjnzw7Q/s1600/DSCN0117.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-F3b7V-iWlOc/Tiq1lUukCcI/AAAAAAAAA6Y/K4Y4Wjnzw7Q/s200/DSCN0117.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-5YQ47Ubsl4Q/Tiq1l2gvF-I/AAAAAAAAA6c/yRlFuWokGCs/s1600/DSCN0118.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-5YQ47Ubsl4Q/Tiq1l2gvF-I/AAAAAAAAA6c/yRlFuWokGCs/s200/DSCN0118.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&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/-qiHhUMmUHvs/Tiq1mQJPOOI/AAAAAAAAA6g/Nh5N8FM4vg4/s1600/DSCN0120.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-qiHhUMmUHvs/Tiq1mQJPOOI/AAAAAAAAA6g/Nh5N8FM4vg4/s200/DSCN0120.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-UAW5kyh852A/Tiq1mqtaJQI/AAAAAAAAA6k/UaYAp3OT0LE/s1600/DSCN0121.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-UAW5kyh852A/Tiq1mqtaJQI/AAAAAAAAA6k/UaYAp3OT0LE/s200/DSCN0121.JPG" width="150" /&gt;&lt;/a&gt;&lt;/div&gt;&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/-lpwLrivlNXc/Tiq1nExFX2I/AAAAAAAAA6o/RIFHdNwzDuE/s1600/DSCN0122.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-lpwLrivlNXc/Tiq1nExFX2I/AAAAAAAAA6o/RIFHdNwzDuE/s200/DSCN0122.JPG" width="150" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-Vr-SBUOWhhc/Tiq1noQggmI/AAAAAAAAA6s/Ks5Rc4vhp8s/s1600/DSCN0123.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-Vr-SBUOWhhc/Tiq1noQggmI/AAAAAAAAA6s/Ks5Rc4vhp8s/s200/DSCN0123.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&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/-eUNJVzv_CJo/Tiq1n4_K5oI/AAAAAAAAA6w/J-QhrM9D4c4/s1600/DSCN0124.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-eUNJVzv_CJo/Tiq1n4_K5oI/AAAAAAAAA6w/J-QhrM9D4c4/s200/DSCN0124.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-IYb-xXlGDDs/Tiq1ofWZ1pI/AAAAAAAAA60/OGm8gt8VE0c/s1600/DSCN0125.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-IYb-xXlGDDs/Tiq1ofWZ1pI/AAAAAAAAA60/OGm8gt8VE0c/s200/DSCN0125.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&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/-PkzxdlCo6YY/Tiq1oqISHbI/AAAAAAAAA64/xr8kGmq673Y/s1600/DSCN0126.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-PkzxdlCo6YY/Tiq1oqISHbI/AAAAAAAAA64/xr8kGmq673Y/s200/DSCN0126.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-ZbSfPPm4vxM/Tiq1pJtZ_3I/AAAAAAAAA68/5VvfYow6WAo/s1600/DSCN0127.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-ZbSfPPm4vxM/Tiq1pJtZ_3I/AAAAAAAAA68/5VvfYow6WAo/s200/DSCN0127.JPG" style="cursor: move;" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;Retromuseo&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&amp;nbsp;&lt;a href="http://2.bp.blogspot.com/-nk-F8X5N4QE/TivcelcdTVI/AAAAAAAAA80/GWRb3uZVt3Y/s1600/DSCN0169.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-nk-F8X5N4QE/TivcelcdTVI/AAAAAAAAA80/GWRb3uZVt3Y/s200/DSCN0169.JPG" width="150" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-0qEpCTtXXqI/TivdGuId2rI/AAAAAAAAA9I/cE7iUw1cHTM/s1600/DSCN0168.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-0qEpCTtXXqI/TivdGuId2rI/AAAAAAAAA9I/cE7iUw1cHTM/s200/DSCN0168.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;Amstrad PC&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-7iGgDV3AklE/Tivcv4D5L4I/AAAAAAAAA84/GOex2jw8MR0/s1600/DSCN0164.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-7iGgDV3AklE/Tivcv4D5L4I/AAAAAAAAA84/GOex2jw8MR0/s200/DSCN0164.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-Wp2E_OWhOJE/Tivc4HZSx2I/AAAAAAAAA88/7vcZautyl4w/s1600/DSCN0165.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-Wp2E_OWhOJE/Tivc4HZSx2I/AAAAAAAAA88/7vcZautyl4w/s200/DSCN0165.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;Microprocesadores y tarjetas gráficas&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-0NQ4f4WRjhg/Tivc9etSUBI/AAAAAAAAA9A/JioUiSt9-Kk/s1600/DSCN0166.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-0NQ4f4WRjhg/Tivc9etSUBI/AAAAAAAAA9A/JioUiSt9-Kk/s200/DSCN0166.JPG" width="150" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-1GBDFWGwNv8/TivdCIRl6aI/AAAAAAAAA9E/E9elmqyiY08/s1600/DSCN0167.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-1GBDFWGwNv8/TivdCIRl6aI/AAAAAAAAA9E/E9elmqyiY08/s200/DSCN0167.JPG" width="150" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;Discos duros y sistemas operativos Linux&lt;/div&gt;&lt;br /&gt;
Tampoco falta la zona donde den la manduca donde están los comedores, cafetería y un puesto de telepizza.&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/-rARRZf8Qa-g/TireM9kB-qI/AAAAAAAAA8w/gmrbCH6Er6s/s1600/DSCN0154.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-rARRZf8Qa-g/TireM9kB-qI/AAAAAAAAA8w/gmrbCH6Er6s/s200/DSCN0154.JPG" 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://4.bp.blogspot.com/-52o28vCMbc0/Tiq4oUc2FHI/AAAAAAAAA8Y/vMv43hFhWrQ/s1600/DSCN0140.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-52o28vCMbc0/Tiq4oUc2FHI/AAAAAAAAA8Y/vMv43hFhWrQ/s200/DSCN0140.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-u86YkXDATGw/TirPFkQX3KI/AAAAAAAAA8g/SpQb7XZqjjg/s1600/DSCN0153.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-u86YkXDATGw/TirPFkQX3KI/AAAAAAAAA8g/SpQb7XZqjjg/s200/DSCN0153.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
La organización ha preparado para los asistentes numerosas actividades, competiciones, concursos, stands, conferencias, escena, talleres y juegos para todos los gustos e intereses que sin duda mantienen a los asistentes ocupados en todo momento. El número de fotos y vídeos que he sacado son muchos (pero menos de los que pensaba), demasiado para incluirlo todo en una entrada aún así os pongo una muestra representativa, casi completa, para que las imágenes sean ellas mismas las que os cuenten las cosas como habéis ido observando.&lt;br /&gt;
&lt;br /&gt;
Competiciones en las que los profesionales se dedican a &lt;a href="http://www.euskal.org/es/juegos.html"&gt;competir entre ellos en diferentes juegos&lt;/a&gt; como Gran Turismo 5, FIFA 211, Super Street Fighter IV Arcade, Moto GP, Counter Strike 1.6, Starcraft 2 y otros cuantos y en diferentes plataformas como XBOX 360, PS3 y PC. Los primeros clasificados obtienen incluso premios en metálico o regalos, algunos individualmente o en equipo vienen en gran medida a competir en el gamegune.&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/-RNKApLZ1PHU/TirPLNxOc0I/AAAAAAAAA8k/1pL3O5tuzpM/s1600/DSCN0159.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-RNKApLZ1PHU/TirPLNxOc0I/AAAAAAAAA8k/1pL3O5tuzpM/s200/DSCN0159.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-7zmzkUK8aHI/TirPLiu0UGI/AAAAAAAAA8o/DCCgS7cs0T0/s1600/DSCN0160.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-7zmzkUK8aHI/TirPLiu0UGI/AAAAAAAAA8o/DCCgS7cs0T0/s200/DSCN0160.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
En el apartado de &lt;a href="http://www.euskal.org/es/escena.html"&gt;escena&lt;/a&gt; hay diferentes modalidades de creación artística desde gráficos 2D, 3D, intros, animación 3D y música. Aquí tenéis una pequeña muestra de la proyección de los trabajos realizados de Fast 3D, Wild Compo y Animación 3D.&lt;br /&gt;
&lt;br /&gt;
Entre tanto fan del &lt;a href="http://www.euskal.org/es/hardware.html"&gt;hardware&lt;/a&gt; no podrían faltar aquellos que personalizan sus equipos de manera extrema y presentan sus trabajos en los concursos de modding. También en el apartado hard pueden asistir al taller de &lt;a href="http://www.arduino.cc/es/"&gt;Arduino&lt;/a&gt; para introducirse en esta plataforma de hardware abierta.&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/-2E34Ir90uCw/Tiq3PCMnDNI/AAAAAAAAA74/7OYhwPCtR3k/s1600/DSCN0107.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-2E34Ir90uCw/Tiq3PCMnDNI/AAAAAAAAA74/7OYhwPCtR3k/s200/DSCN0107.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-J_2oxgyCI7k/Tiq3Q-H-McI/AAAAAAAAA8I/rx_oDUygPsc/s1600/DSCN0143.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-J_2oxgyCI7k/Tiq3Q-H-McI/AAAAAAAAA8I/rx_oDUygPsc/s200/DSCN0143.JPG" 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://2.bp.blogspot.com/-YSXPq8ve04g/Tiq3PbZtSDI/AAAAAAAAA78/CHyKD4meUHM/s1600/DSCN0132.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-YSXPq8ve04g/Tiq3PbZtSDI/AAAAAAAAA78/CHyKD4meUHM/s200/DSCN0132.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-PP7ZMCdtZsY/Tiq3P0FqxyI/AAAAAAAAA8A/7hvZL7nVduA/s1600/DSCN0134.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-PP7ZMCdtZsY/Tiq3P0FqxyI/AAAAAAAAA8A/7hvZL7nVduA/s200/DSCN0134.JPG" 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://4.bp.blogspot.com/-6Lm5PcXGtHs/Tiq3QdfVq5I/AAAAAAAAA8E/tmeKJqzQRcY/s1600/DSCN0136.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-6Lm5PcXGtHs/Tiq3QdfVq5I/AAAAAAAAA8E/tmeKJqzQRcY/s200/DSCN0136.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-T0q7WjpSJFI/Tiq3RDp6XnI/AAAAAAAAA8M/YZLhKvzJPRQ/s1600/DSCN0144.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-T0q7WjpSJFI/Tiq3RDp6XnI/AAAAAAAAA8M/YZLhKvzJPRQ/s200/DSCN0144.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Los &lt;a href="http://www.euskal.org/es/actividades-y-concursos.html"&gt;concursos&lt;/a&gt; son muchos Air Guitar, Futbol 3, disfraces, carrera de sacos, Libdub, Fast crimping, de disfraces, Frikitest y el ya clásico lanzamiento de CD y DVD además de algunos otros. También hay concursos dirigidos al software libre Hack it para resolver una serie de problemas, AI Contest en el que programar la inteligencia artificial de una moto del clásico juego de las motos de luz de Tron, Foss Coding en el que se plantea un problema y se ha de resolver con herramientas de software libre. La euskal cuenta con la &amp;nbsp;presencia por parte de la Free Software Foundation donde se pueden adquirir camisetas.&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/-lnhNDBNY-x4/Tiq2vyZKDHI/AAAAAAAAA7w/wOq0uDpNjNs/s1600/DSCN0113.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://2.bp.blogspot.com/-lnhNDBNY-x4/Tiq2vyZKDHI/AAAAAAAAA7w/wOq0uDpNjNs/s200/DSCN0113.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Los mainframes no son cosa del pasado, siguen ahí y por lo visto son de mucha utilidad para tratar gran cantidad de información. Hasta ahora no había visto ninguno en persona ya que suelen estar escondidos en algún recóndito sótano. Aquí los que hacen la euskal posible.&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/-BD46AcnpNec/Tiq3rX5RyqI/AAAAAAAAA8Q/JHqT1zDzbxE/s1600/DSCN0103.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://3.bp.blogspot.com/-BD46AcnpNec/Tiq3rX5RyqI/AAAAAAAAA8Q/JHqT1zDzbxE/s200/DSCN0103.JPG" width="200" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-hP24iMecUIE/Tiq3r4TNAQI/AAAAAAAAA8U/6GSsd4qv3W0/s1600/DSCN0105.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="150" src="http://4.bp.blogspot.com/-hP24iMecUIE/Tiq3r4TNAQI/AAAAAAAAA8U/6GSsd4qv3W0/s200/DSCN0105.JPG" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Después de pasar muchas horas delante del ordenador a lo largo de los cuatro días hay varios &lt;a href="http://www.euskal.org/es/talleres.html"&gt;talleres&lt;/a&gt; a los que asistir desde relacionados con juegos, hacking, mainframes, gadgets y programación de videojuegos.&lt;br /&gt;
&lt;br /&gt;
El asistir a la Euskal ha sido toda una experiencia, un montón de gente con los mismos intereses y un montón de cosas por ver y hacer además de muy divertido sobre todo para los jugones aunque también se organizan cosas para programadores, artistas, .... El año que viene no se si asistiré o lo haré los cuatro días, me parece demasiado para mí pero un día puede volver a estar bien.&lt;br /&gt;
&lt;br /&gt;
Seguramente haya habido muchos detalles que me he perdido pero me ha gustado mucho lo que he visto, para finalizar un vídeo que gusta bastante por aquí y por el cual puedes escuchar un Patxiiiiiiiiiiiiiiiiiiiiiiii! en cualquier momento,&amp;nbsp;&lt;a href="http://goo.gl/uy324"&gt;http://goo.gl/uy324&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Ya puedo decir que estuve allí. ¡Hasta el año que viene si se da la oportunidad!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-1893061755351317672?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/N_jj62gTQ7g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/1893061755351317672/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/07/un-dia-en-la-euskal-encounter-19.html#comment-form" title="4 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1893061755351317672?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/1893061755351317672?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/N_jj62gTQ7g/un-dia-en-la-euskal-encounter-19.html" title="Un día en la Euskal Encounter 19" /><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/-XcMwHk7AABo/TiqxFNgHm7I/AAAAAAAAA50/Ot6_eSvAkEA/s72-c/euskal-encounter.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/07/un-dia-en-la-euskal-encounter-19.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4EQXg5eyp7ImA9WhdTGU8.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-5025726689411196533</id><published>2011-07-17T20:20:00.005+02:00</published><updated>2011-07-17T20:21:40.623+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-17T20:21:40.623+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="programación" /><title>Vídeo promocional sobre Arch Linux</title><content type="html">Después de los &lt;a href="http://elblogdepicodev.blogspot.com/2011/07/motivos-para-elegir-la-distribucion.html"&gt;motivos de por qué elegir Arch Linux&lt;/a&gt; he hecho un pequeño vídeo que resume algunos aspectos de está fantástica distribución, para aquellos que prefieran ver en vez de leer y también para poder hacerlo llegar no solo a los que visitan mi blog sino también para aquellos que visitan youtube.&lt;br /&gt;
&lt;br /&gt;
Rolling Release, KISS, número de paquetes disponibles, aplicaciones de usuario, aplicaciones para desarrollo, personalización, rapidez y ligereza, adaptación a las preferencias del usuario y su gran comunidad son algunas características que destaco de esta distribución. El vídeo es muy simple porque es mi primer vídeo, no tengo muchas capacidades gráficas, ni una gran creatividad y tampoco PiTiVI permite muchas opciones (esperemos que según vayan saliendo nuevas versiones le añadan funcionalidades y corrijan errores ya que&amp;nbsp;no ha juntado bien las diapositivas)&amp;nbsp;pero espero que contenga los&amp;nbsp;aspectos importantes de Arch Linux, si creeís que falta o tenéis alguna aportación solo tenéis que sugerírmelo y haré lo que pueda por añadirlo. El vídeo no es definitivo y a medida que se me ocurra o me comentéis alguna modificación la iré añadiendo.&lt;br /&gt;
&lt;br /&gt;
El vídeo está completamente hecho con software libre. PiTiVi, Inkscape, GIMP y por supuesto Arch Linux.&lt;br /&gt;
&lt;br /&gt;
Espero que a alguien le aníme a pasarse a esta distribución, si es el caso puedes visitar las guía de instalación de Arch Linux que he escrito. Está dividida en dos partes: &lt;a href="http://elblogdepicodev.blogspot.com/2010/05/guia-instalacion-arch-linux-parte-i.html"&gt;Instalación base&lt;/a&gt; y &lt;a href="http://elblogdepicodev.blogspot.com/2010/07/guia-instalacion-arch-linux-parte-ii.html"&gt;Entorno gráfico y programas&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Sin más, espero que os guste :).&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://1.gvt0.com/vi/VFMZ8vjYIz0/0.jpg" height="384" width="512"&gt;&lt;param name="movie" value="http://www.youtube.com/v/VFMZ8vjYIz0&amp;fs=1&amp;source=uds" /&gt;



&lt;param name="bgcolor" value="#FFFFFF" /&gt;



&lt;embed width="512" height="384"  src="http://www.youtube.com/v/VFMZ8vjYIz0&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
Mientras estaba subiendo mi video me he encontrado con otros que me han parecido interesantes en este caso promocionando Linux (Linux, la libertad de elegir y Nosotros somos Linux ¿y tú?).&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style="text-align: center;"&gt;
&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://2.gvt0.com/vi/GRKuatGmZNo/0.jpg" height="384" width="512"&gt;&lt;param name="movie" value="http://www.youtube.com/v/GRKuatGmZNo&amp;fs=1&amp;source=uds" /&gt;



&lt;param name="bgcolor" value="#FFFFFF" /&gt;



&lt;embed width="512" height="384"  src="http://www.youtube.com/v/GRKuatGmZNo&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style="text-align: center;"&gt;
&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://3.gvt0.com/vi/Ozmu-CjqrCM/0.jpg" height="384" width="512"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Ozmu-CjqrCM&amp;fs=1&amp;source=uds" /&gt;



&lt;param name="bgcolor" value="#FFFFFF" /&gt;



&lt;embed width="512" height="384"  src="http://www.youtube.com/v/Ozmu-CjqrCM&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-5025726689411196533?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/51pZqhKqWnY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/5025726689411196533/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/07/video-promocional-sobre-arch-linux.html#comment-form" title="3 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5025726689411196533?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/5025726689411196533?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/51pZqhKqWnY/video-promocional-sobre-arch-linux.html" title="Vídeo promocional sobre Arch Linux" /><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><thr:total>3</thr:total><feedburner:origLink>http://elblogdepicodev.blogspot.com/2011/07/video-promocional-sobre-arch-linux.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4GRnY9fCp7ImA9WhdTE0U.&quot;"><id>tag:blogger.com,1999:blog-2347950092468997900.post-4141898993720465652</id><published>2011-07-08T13:25:00.011+02:00</published><updated>2011-07-11T13:32:07.864+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-11T13:32:07.864+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="planeta arch linux" /><category scheme="http://www.blogger.com/atom/ns#" term="planeta linux" /><category scheme="http://www.blogger.com/atom/ns#" term="programación" /><title>Usar Apache Tapestry 5 con Groovy (u otros lenguajes de la JVM)</title><content type="html">&lt;div class="separator" style="clear: both; float: right;"&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;
Una de las caracteristicas de la que presume Apache Tapestry 5 es ser poliglota. Esto significa que podemos desarrollar nuestra aplicacción no solo con el lenguaje de programación Java sino con cualquier otro que se ejecute sobre la máquina virtual, como Groovy, Scala o una mezcla de ellos, aprovechando algunas características de estos lenguajes que todavía no posee Java, como las closures y una API ampliada que hace uso de ellas y que ayuda a reducir el código de la aplicación además de hacerlo más legible. Personalmente me gusta mucho Tapestry pero también me gustan algunas características de Groovy como lenguaje, ahora juntando las dos cosas ambas se me hacen aún más interesantes y con muchas más posibilidades.&lt;br /&gt;
&lt;br /&gt;
Para probar esta característica he actualizado la aplicación&amp;nbsp;(ahora con Tapestry 5.2)&amp;nbsp;que utilicé en &lt;a href="http://elblogdepicodev.blogspot.com/2010/06/hola-mundo-con-apache-tapestry-51-en.html"&gt;una entrada anterior&lt;/a&gt;, &lt;a href="http://hello-world-tapestry5-gae.appspot.com/"&gt;Hola mundo con Tapestry 5 en el Google App Engine&lt;/a&gt;, en la que se puede ver la aplicación funcionando en el Google App Engine programada en Java junto con la misma versión con las páginas y componentes programada en Groovy.&lt;br /&gt;
&lt;br /&gt;
Sin embargo, a decir verdad, esto no creo que sea una característica de Apache Tapestry de por si sino más bien de la máquina virtual Java ya que intuyo que la misma técnica se podría aplicar a otros frameworks. La magia consiste en la forma de compilar las clases del projecto, en vez de utilizar la tarea de Ant javac para compilar hay que utilizar groovyc.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;&amp;lt;target name="compile"&amp;gt;	
        &amp;lt;path id="compile.lib.path"&amp;gt;
            &amp;lt;fileset dir="lib" includes="*.jar"/&amp;gt;
        &amp;lt;/path&amp;gt;
        &amp;lt;path id="groovy.lib.path"&amp;gt;
            &amp;lt;fileset dir="lib" includes="groovy-all-1.8.0.jar"/&amp;gt;
        &amp;lt;/path&amp;gt;
		&amp;lt;taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.lib.path"/&amp;gt;

		&amp;lt;mkdir dir="build/classes"/&amp;gt;
		&amp;lt;!-- Compilar las clases (solo java) --&amp;gt; 
&amp;lt;!--
		&amp;lt;javac srcdir="src"		
			destdir="build/classes"
			source="1.5"
			classpathref="compile.lib.path"
			includeAntRuntime="false"/&amp;gt;
--&amp;gt;
		&amp;lt;!-- Compilar las clases java y groovy --&amp;gt;
		&amp;lt;groovyc srcdir="src" 
			destdir="build/classes"
			classpathref="compile.lib.path"&amp;gt;
			&amp;lt;javac
				source="1.5"
				debug="on"/&amp;gt;
		&amp;lt;/groovyc&amp;gt;
    &amp;lt;/target&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
En este ejemplo del componente Reloj las diferencias con Java no son muchas pero se puede ver que es código Groovy, ya que no utiliza ; para los finales de sentencia y se pueden acceder a las propiedades sin el método get. (como en el caso componentResources.id)&lt;br /&gt;
&lt;br /&gt;
&lt;div class="codigo"&gt;
&lt;pre&gt;package com.blogspot.elblogdepicodev.tapestry.helloWorld.components

import org.apache.tapestry5.Asset
import org.apache.tapestry5.ComponentResources
import org.apache.tapestry5.MarkupWriter
import org.apache.tapestry5.annotations.Environmental
import org.apache.tapestry5.annotations.Path
import org.apache.tapestry5.annotations.SupportsInformalParameters
import org.apache.tapestry5.ioc.annotations.Inject
import org.apache.tapestry5.services.javascript.JavaScriptSupport

@SupportsInformalParameters
public class RelojGroovy {

    @Inject
    @Path("classpath:com/blogspot/elblogdepicodev/tapestry/helloWorld/components/Reloj.js")
    private Asset script

    @Environmental
    private JavaScriptSupport javaScriptSupport
    
    @Inject
    private ComponentResources componentResources

    protected void setupRender() {
        javaScriptSupport.importJavaScriptLibrary(script)
    }

    protected boolean beginRender(MarkupWriter writer) {
        def id = componentResources.id
        
        writer.element("span", "id", id)
        componentResources.renderInformalParameters(writer)
		return false
    }

    protected boolean afterRender(MarkupWriter writer) {
        def id = componentResources.id

        writer.end()
        javaScriptSupport.addScript("var %s = new Reloj('%s')", id, id)       
		return true
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Una vez compiladas las clases para la máquina virtual ya no hay diferencia entre las clases compiladas con Java y las clases compiladas con Groovy y por este motivo podemos hacer uso en Tapestry otros lenguajes además de Java. Además de compilar con groovyc lo único especial que deberemos hacer es incluir en la aplicación la librería groovy-all-1.8.0.jar para que las clases groovy funcionen correctamente.&lt;br /&gt;
&lt;br /&gt;
Referencia:&lt;br /&gt;
&lt;a href="http://elblogdepicodev.blogspot.com/2010/05/documentacion-sobre-apache-tapestry.html"&gt;Documentación sobre Apache Tapestry&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://hello-world-tapestry5-gae.appspot.com/"&gt;Hola mundo con Tapestry 5 en el Google App Engine&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://docs.google.com/uc?id=0B4yquUFv3csrZTlhMThkYzktZmUxOC00YzhhLWE0ZWItNzRkMWE5Mzk2ZDNh&amp;amp;export=download&amp;amp;hl=es"&gt;Codigo fuente ejemplo Hola Mundo con Apache Tapestry 5 en Google App Engine&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2347950092468997900-4141898993720465652?l=elblogdepicodev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/HkNj/~4/vJJRfSjz_Jg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://elblogdepicodev.blogspot.com/feeds/4141898993720465652/comments/default" title="Enviar comentarios" /><link rel="replies" type="text/html" href="http://elblogdepicodev.blogspot.com/2011/07/usar-apache-tapestry-5-con-groovy-u.html#comment-form" title="0 comentarios" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4141898993720465652?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2347950092468997900/posts/default/4141898993720465652?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/HkNj/~3/vJJRfSjz_Jg/usar-apache-tapestry-5-con-groovy-u.html" title="Usar Apache Tapestry 5 con Groovy (u otros lenguajes de la JVM)" /><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/2011/07/usar-apache-tapestry-5-con-groovy-u.html</feedburner:origLink></entry></feed>

