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

<channel>
	<title>4 bits blog &#187; Java</title>
	<atom:link href="http://blog.4bits.es/category/lenguajes/java/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.4bits.es</link>
	<description>Ahora en 16 colores</description>
	<lastBuildDate>Thu, 05 Aug 2010 13:52:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>El extraño bug del Calendar</title>
		<link>http://blog.4bits.es/el-extrano-bug-del-calendar/</link>
		<comments>http://blog.4bits.es/el-extrano-bug-del-calendar/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 19:15:05 +0000</pubDate>
		<dc:creator>Lek</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=466</guid>
		<description><![CDATA[Cuando empezaba a programar con C y C++ recuerdo los quebraderos de cabeza para comprobar que los valores &#8220;entraban&#8221; dentro de las variables y evitar desbordamientos problemáticos. Java solucionó esto haciendo que, si el valor se pasaba del rango de la variable, se reiniciaba el contador. Es decir, que si queremos poner el valor 212 [...]]]></description>
			<content:encoded><![CDATA[<p>Cuando empezaba a programar con C y C++ recuerdo los quebraderos de cabeza para comprobar que los valores &#8220;entraban&#8221; dentro de las variables y evitar desbordamientos problemáticos. Java solucionó esto haciendo que, si el valor se pasaba del rango de la variable, se reiniciaba el contador. Es decir, que si queremos poner el valor 212 a un fichero byte (-128 a 127), Java nos muestra en realidad -44.</p>
<p>Esto, que en principio puede parecer un gran avance porque evita problemas de seguridad en los programas (uno de los motivos por los que en Java no se pueden manejar punteros), al final se demuestra que es un nido de bugs. Porque <strong>212 no son -44</strong>. Y esto puede ser un auténtico problema en algo tan visible como una fecha.</p>
<p>El mes de marzo es un mes muy especial. Su mes anterior es de longitud variable (28 ó 29 días) y el siguiente es de 30, siendo el propio marzo de 31. Ved el siguiente código:</p>
<pre class="brush:java">Calendar cal = new GregorianCalendar();
cal.set (Calendar.DAY_OF_MONTH, 30);
cal.set (Calendar.MONTH, 2);
System.out.println (cal.getTime ());
System.out.println (cal.getActualMaximum (Calendar.DAY_OF_MONTH));
cal.set (Calendar.MONTH, cal.get(Calendar.MONTH) -1);
System.out.println (cal.getActualMaximum (Calendar.DAY_OF_MONTH));</pre>
<p>Esencialmente, lo que hace es ponernos en 30 de marzo de este año y sacar por pantalla los siguientes datos:</p>
<ul>
<li>Fecha actual</li>
<li>Número de días del mes de marzo</li>
<li>Número de días del mes de&#8230; ¿febrero?</li>
</ul>
<p>Java, en su excelsa sabiduría, <strong>ha decidido que en este caso que 2-1=2</strong>. Porque al restar uno al mes nos quedamos en 30 de febrero o, lo que es lo mismo, 2 de marzo. Esta chorrada (obviamente en un código más complejo) nos tuvo en el trabajo una mañana entretenidos porque el número de días de los meses siempre eran 30 ó 31, incluído un <strong>inexistente febrero</strong>.</p>
<p>En parte fue una suerte que estuviéramos a 30 de marzo y no en 2 de abril, en cuyo caso nos daríamos cuenta del problema casi un año después de sacar el proyecto a producción. A veces Java es realmente odioso con su &#8220;<em>deja que la JVM piense por ti</em>&#8220;.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/el-extrano-bug-del-calendar/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Cómo proteger las contraseñas de los usuarios</title>
		<link>http://blog.4bits.es/como-proteger-las-contrasenas-de-los-usuarios/</link>
		<comments>http://blog.4bits.es/como-proteger-las-contrasenas-de-los-usuarios/#comments</comments>
		<pubDate>Sun, 22 Nov 2009 18:53:58 +0000</pubDate>
		<dc:creator>Fran</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Seguridad]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=396</guid>
		<description><![CDATA[Voy a recuperar otro de mis documentos perdidos en Google Docs (el anterior fue el de las autotools), esta vez se trata de un pequeño manual sobre cómo proteger las contraseñas de lo usuarios de cualquier aplicación que se programe, el manual es una traducción al español del artículo How to encrypt user passwords de [...]]]></description>
			<content:encoded><![CDATA[<p>Voy a recuperar otro de mis documentos perdidos en Google Docs (el anterior fue el de <a href="http://blog.4bits.es/autotools/">las autotools</a>), esta vez se trata de un pequeño manual sobre cómo proteger las contraseñas de lo usuarios de cualquier aplicación que se programe, el manual es una traducción al español del artículo <a href="http://www.jasypt.org/howtoencryptuserpasswords.html">How to encrypt user passwords</a> de Daniel Fernandez.</p>
<h3>1. Visión general</h3>
<p><strong>Casi todas las aplicaciones web modernas necesitan</strong>, de un modo u otro, <strong>cifrar las contraseñas de sus usuarios.</strong> Se podría decir que, desde el momento en que la aplicación tiene usuarios, y los usuarios se identifican usando una contraseña, <strong>esas contraseñas se deben guardar usando algún tipo de cifrado.</strong></p>
<p>Hay muchas razones básicas para esto: las bases de datos pueden estar comprometidas, y por tanto también las comunicaciones. Pero la razón más importante es que se tiene que pensar que las contraseñas de los usuarios son datos personales. Sus contraseñas son sus claves para su privacidad, por tanto son personales, y nadie tiene el derecho de conocerlas. Y se debe cumplir esto si se quiere ganar la confianza de los usuarios.</p>
<h3>2. El algoritmo</h3>
<p>Ahora que se quieren cifrar las contraseñas, se debe saber cómo. Aquí entra la primera regla:</p>
<blockquote><p>I. Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o <em>hash</em>).</p></blockquote>
<p>Esto se debe a que, excepto para algunos escenarios específicos, no hay ninguna razón para que una contraseña sea descifrada. Si se cifran las contraseñas usando cifrados basados en claves (una técnica de dos sentidos) y un atacante consigue saber la clave de cifrado, se revelarán todas las contraseñas. Si no se tiene una clave para el cifrado, el riesgo desaparece, y el atacante tendrá que confiar en el uso de técnicas de fuerza bruta o similares.</p>
<p>Pero, ¿qué ocurre si un usuario pierde su contraseña? ¿No podrá recuperarla? La respuesta es NO. No sólo no se puede recuperar, si no que ni siquiera existe una manera de leer/conocer/ver las contraseñas de los usuarios, no importa si se es el administrador del sistema. Si un usuario pierde su contraseña, sólo se podrá reiniciarla a un nuevo valor y enviárselo por algún canal seguro al usuario, y pidiéndole que la cambie lo más pronto que pueda.</p>
<p>Ahora que está claro que las funciones resumen son necesarias en las contraseñas, ¿cuál algoritmo se debería usar? Hay bastantes, y dependen de las necesidades de cada uno. Los más usados son:</p>
<ul>
<li>Algoritmo MD5.</li>
<li>Familia SHA: SHA-1 y la variantes de SHA-2 (SHA-224, SHA-256, SHA-384 y SHA-512)</li>
</ul>
<p>En la mayoría de los casos, MD5 o SHA-1 serán elecciones adecuadas para los resúmenes de contraseñas, aunque aplicar estos algoritmos no será suficiente, como se verá a continuación.</p>
<p>Cuando se dice que se deberían usar funciones resumen para los cifrados de las contraseñas, y dado que estas funciones resumen son técnicas de un sentido, la siguiente pregunta es: «Si no se pueden descifrar las contraseñas, ¿cómo se comprueba que un usuario tiene una contraseña correcta?»</p>
<p>Hay una respuesta muy simple a esta cuestión, que será la segunda regla:</p>
<blockquote><p>II. La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.</p></blockquote>
<p>Lo que significa que, una vez los usuarios han introducido sus contraseñas en la identificación, se realizará la función resumen sobre la contraseña introducida con el mismo algoritmo que se ha usado con las contraseñas guardadas, y se compararán ambos resultados. Como estas funciones garantizan que dos entradas iguales obtienen el mismo resultado (lo que no es cierto en el sentido contrario), si los resultados coinciden se considerará que la contraseña introducida por el usuario es correcta.</p>
<h3>3. Mejorando la seguridad de los resúmenes</h3>
<p>Todos los algoritmos de funciones resumen mencionados anteriormente comparten una característica: son públicos. Son algoritmos muy conocidos y ampliamente implementados, por lo que cualquiera puede usarlos.</p>
<p>Si cualquiera puede usar el mismo algoritmo que la aplicación, y por alguna razón los atacantes pueden ver la base de datos de los resúmenes de las contraseñas, ¿cómo se puede estar seguro que los atacantes no serán capaces de conseguir alguna contraseña de los usuarios probando todas las posibilidades hasta que encuentren algún resumen que coincida con uno de los guardados?</p>
<p>La respuesta es que no se puede. Pero se puede hacer que todo eso sea una tarea enorme y con un gran tiempo de realización, de modo que no resulte útil, a menos que puedan esperar toda la eternidad. Para lograr esto, se van a explicar dos conceptos: «salt» y el contador de iteración.</p>
<h4>3.1. «Salt»</h4>
<p>«Salt» es una secuencia de bytes que se añaden a la contraseña antes de conseguir su resumen. Esto hace que estos resúmenes sean diferentes a los que deberían ser usando sólo la contraseña, y como resultado se consigue protección contra los ataques de diccionario. Se pueden seguir dos estrategias para usar «salt»:</p>
<ul>
<li>Usar un «salt» fijo, una secuencia de bytes que se utilizarán para resumir cualquier contraseña. Se puede guardar oculto el «salt» y considerarlo un valor añadido de seguridad, pero esto puede hacer que el sistema se vuelva más vulnerable a ataques de cumpleaños y, en general, a ataques dirigidos contra la base de datos de contraseñas.</li>
<li>Usar un «salt» variable, que normalmente es una opción segura (especialmente si es aleatorio). Este se genera independientemente para cada contraseña a resumir, y permite que cada contraseña guardada esté desacoplada de las demás, creando una gran protección general y un alta seguridad contra ataques dirigidos contra la base de datos de contraseñas.</li>
</ul>
<p>En la práctica, un «salt» aleatorio (o variable) es la mejor idea porque, aunque al generarse aleatoriamente tendrá que guardarse junto al resumen de la contraseña (por lo que cualquiera podrá recuperarlo) y esto hará que sea trivial para un atacante conocerlo, permitirá que cada contraseña de usuario sea independiente del resto, por lo que tendrán que atacarse una por una.</p>
<p>Se tiene que pensar que, si se usa un «salt» fijo y el atacante consigue saberlo, la seguridad de toda la base de datos de contraseñas será nula. Y hay muchos métodos para que el atacante pueda conocer este «salt», como aplicar fuerza bruta en sus contraseñas o en contraseñas que tenga de algún usuario válido. En un escenario de un «salt» fijo, una contraseña débil podría hacer que el sistema de contraseñas fuera débil.</p>
<p>Sin embargo, si se quiere mantener una parte del «salt» en secreto, se puede usar una técnica que mezcle las otras dos, usando un «salt» compuesto de una parte fija y otra aleatoria, guardando los bytes aleatorios junto al resumen de la contraseña.</p>
<p>El tamaño mínimo de un «salt» es de 8 bytes. Si se usa una opción mixta, al menos 8 bytes deberían ser aleatorios.</p>
<p>Así, se puede definir la tercera regla:</p>
<blockquote><p>III. Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.</p></blockquote>
<h4>3.2. El contador de iteración</h4>
<p>El contador de iteración es el número de veces que la función <em>hash</em> que realiza el resumen se aplica a sus propios resultados.</p>
<p>Esto significa que, una vez se ha seleccionado un «salt» y concatenado con la contraseña, se deberá aplicar la función <em>hash</em> (por ejemplo, MD5), conseguir el resultado, y pasarlo de nuevo como entrada a la misma función, y hacer lo mismo las veces que indique el contador de iteración.</p>
<p>El número mínimo recomendado de iteraciones es 1.000, y esto proporcionará una buena seguridad extra. Hay que pensar que, cuando se crea un único resumen de una contraseña para un nuevo usuario, la diferencia entre aplicar la función <em>hash</em> una vez o 1.000 veces no será un problema, tal vez doscientos milisegundos, pero los atacantes deberán tener que generar una enorme cantidad de intentos de resumen mediante fuerza bruta y, para un atacante, la diferencia entre aplicar la función <em>hash</em> una vez y aplicarla mil veces para cada intento será un gran problema computacional.</p>
<p>Así, se obtiene la cuarta regla:</p>
<blockquote><p>IV. Iterar la función <em>hash</em> al menos 1.000 veces.</p></blockquote>
<h3>4. Secuencia de traducción de caracteres a secuencia de bytes</h3>
<p>Antes de poder guardar correctamente los resúmenes de las contraseñas, hay algo que se debería tener en cuenta, que es la diferencia entre cadenas de caracteres y secuencias de bytes: dos cadenas de caracteres se pueden representar con diferentes secuencias de bytes dependiendo de la codificación empleada (ISO-8859-1, UTF-8, &#8230;)</p>
<p>Esto afecta, ya que las contraseñas suelen ser cadenas de caracteres, pero las funciones <em>hash</em> trabajan a nivel de byte.</p>
<h4>4.1. Problemas codificando la contraseña introducida</h4>
<p>En el supuesto de que un nuevo usuario se identifique con una contraseña que no contiene caracteres ASCII, y que la aplicación de identificación, que se está ejecutándo en Windows 2000 en Europa Occidental, convierta la cadena de caracteres a bytes sin elegir una codificación específica para la operación, e incluso usando la que haya de forma predeterminada, en Windows suele ser ISO-8859-1. De modo que el resumen se realice sobre esa secuencia de bytes y quede almacenado.</p>
<p>Entonces, este usuario vuelve de nuevo y esta vez no visita la aplicación de identificación anterior, en vez de eso usa cualquier otra aplicación con la misma base de datos de contraseñas pero en Linux. El usuario introduce correctamente su contraseña y &#8230; ¡no coincide!</p>
<p>¿Por qué? Porque la mayoría de los sistemas Linux, usan UTF-8 como codificación predeterminada, y las contraseñas que ha introducido el usuario en ambos casos se han codificado en diferentes secuencias de bytes, afectando a la correcta concordancia de las contraseñas.</p>
<p>Cómo se puede resolver esto, pues utilizando una codificación fija para las codificaciones de las cadena de caracteres a secuencia de bytes. Así se enuncia la quinta regla:</p>
<blockquote><p>V. Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.</p></blockquote>
<p>Si se usa Java, donde las cadenas de caracteres (clases <code>String</code>) se codifican independientemente (aunque usan de fondo UTF-16), no habrá que preocuparse de si la aplicación usa ISO-8859-1 o alguna otra codificación en lugar de UTF-8 para su interfaz de usuario, al igual que no será necesario que la codificación para los resúmenes de las contraseñas coincidan con las de la interfaz de usuario. Sólo será necesario que la codificación sea una fija, y UTF-8 proporciona un buen equilibrio entre tamaño y conjunto de caracteres.</p>
<h4>4.2. Problemas codificando en el almacenamiento del resumen</h4>
<p>Normalmente se quiere gestionar y almacenar el resumen de contraseñas como una cadena de caracteres, pero la función <em>hash</em> dará como resultado una secuencia de bytes que no necesariamente representará un carácter válido en alguna codificación. Por esto, la secuencia de bytes del resumen no se puede codificar en una cadena de caracteres, habiendo peligro de pérdidas de datos si se realizase.</p>
<p>Aquí es donde entra al rescate la codificación BASE64. Codificando la secuencia de bytes del resumen en BASE64, se asegura que la secuencia de bytes de salida representa una cadena de caracteres US-ASCII válida. Por lo que se podrá codificar una secuencia de bytes en BASE64 en una cadena de caracteres US-ASCII.</p>
<blockquote><p>VI. Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.</p></blockquote>
<h3>5. Resumen de las reglas para el cifrado de contraseñas</h3>
<p>La lista completa de reglas a aplicar:</p>
<ol>
<li>Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o <em>hash</em>).</li>
<li>La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.</li>
<li>Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.</li>
<li>Iterar la función <em>hash</em> al menos 1.000 veces.</li>
<li>Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.</li>
<li>Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.</li>
</ol>
<h3>6. Realizándolo en Java</h3>
<p>La forma más sencilla de <a href="http://www.jasypt.org/encrypting-passwords.html">cifrar contraseñas en Java</a>, mediante las técnicas explicadas es usar Jasypt.</p>
<p>Si no se puede usar jasypt, o si por alguna razón se quiere desarrollar alguna característica de cifrado propia, se necesitarán:</p>
<ul>
<li>La clase <code>java.security.MessageDigest</code> para crear resúmenes. Esta clase permite especificar el algoritmo que se va a usar.</li>
<li>La clase <code>java.security.SecureRandom</code> para generar «salt» aleatorios de una manera segura, usando algoritmos como <code>SHA1PRNG</code>.</li>
<li>El método <code>java.lang.String.getBytes (String charsetName)</code>, para obtener una secuencia de bytes desde una cadena de caracteres, especificando una codificación fija (UTF-8).</li>
<li>La clase <code>org.apache.commons.codec.binary.Base64</code>, parte de la biblioteca Apache Commons-Codec, para realizar codificaciones BASE64 en la salida de la función <em>hash</em>.</li>
</ul>
<p>También, se puede utilizar el código fuente para las clases <code>org.jasypt.digest.StandardByteDigester</code> y <code>org.jasypt.digest.StandardStringDigester</code>, disponible en <a href="http://www.jasypt.org/source-repository.html">el repositorio de jasypt</a>.</p>
<h3>7. Defensa frente a los típicos ataques</h3>
<h4>7.1. Ataques de fuerza bruta</h4>
<p>Realizado sobre: Una única contraseña.</p>
<p>Descripción: El atacante intenta conseguir la contraseña del usuario generando todas las contraseñas posibles, resumiéndolas y probando si coinciden con el resumen de la contraseña del usuario.</p>
<p>Defensa: La iteración de la función <em>hash</em> un número de veces, como 1.000 (mínimo recomendado), el coste de la creación del resumen de la contraseña en el tiempo de identificación del usuario no es significante, pero el coste de un ataque de fuerza bruta por parte de un atacante que genera millones de resúmenes será muy grande. Hay que recordar que una de las mejores maneras de proteger los datos cifrados es haciendo que el coste de romper la seguridad sea más alto de lo que cuesta el esfuerzo.</p>
<h4>7.2. Ataques de diccionario</h4>
<p>Realizado sobre: Cada contraseña o una base de datos de contraseñas</p>
<p>Descripción: El atacante intenta conseguir la contraseña de un usuario comparando sus resúmenes contra un conjunto de resúmenes de contraseñas más probables, normalmente se generan desde una lista de palabras de un diccionario. Este ataque explota una debilidad actual de las aplicaciones, ya que muchos usuarios usan palabras del diccionario como contraseña.</p>
<p>Defensa: Añadiendo un «salt» aleatorio, la debilidad de las contraseñas basadas en diccionario se reduce, y la posibilidad de de que el resumen aparezca en un conjunto de resúmenes previamente creados por el atacante es mínima.</p>
<h4>7.3. Ataques de cumpleaños</h4>
<p>Realizado sobre: La base de datos de las contraseñas.</p>
<p>Descripción: Este ataque explota la paradoja del cumpleaños, la cual en resumen consiste en, teniendo un gran conjunto de resúmenes de contraseñas, las posibilidades de generar una contraseña cuyo resumen coincida con al menos uno de los resúmenes en el conjunto es mucho más alto que lo esperado. Y estas posibilidades aumentan bastante cuando el tamaño del conjunto (el número de usuarios) aumenta.</p>
<p>Defensa: Añadiendo un «salt» aleatorio las posibilidades de que un ataque de cumpleaños tenga éxito son mínimas, porque el atacante tendría que atacar cada contraseña por separado, y no el conjunto de contraseñas, para encontrar una coincidencia. Esto es porque se tendría que encontrar una contraseña que crease el mismo resumen que el del atacante usando el mismo «salt» que se usó para el resumen, y que es diferente para cada contraseña haciendo que el ataque pase a ser de fuerza bruta.</p>
<p>Espero que este pequeño manual os sea de utilidad, a mí me sirvió para mi <abbr title="Proyecto Fin de Carrera">PFC</abbr>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/como-proteger-las-contrasenas-de-los-usuarios/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Internacionalización en Java usando UTF-8</title>
		<link>http://blog.4bits.es/internacionalizacion-en-java-usando-utf-8/</link>
		<comments>http://blog.4bits.es/internacionalizacion-en-java-usando-utf-8/#comments</comments>
		<pubDate>Fri, 23 Oct 2009 10:45:52 +0000</pubDate>
		<dc:creator>Fran</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=354</guid>
		<description><![CDATA[Una de las bondades de Java es que trae un montón de clases que hacen de todo, incluso internacionalización, es decir, poder traducir nuestra aplicación para que se muestre en el idioma adecuado para el usuario. Si alguna vez habéis necesitado internacionalizar en Java, os habréis encontrado con la clase ResourceBundle, esta clase permite la [...]]]></description>
			<content:encoded><![CDATA[<p>Una de las bondades de Java es que trae un montón de clases que hacen de todo, incluso internacionalización, es decir, poder traducir nuestra aplicación para que se muestre en el idioma adecuado para el usuario.</p>
<p>Si alguna vez habéis necesitado internacionalizar en Java, os habréis encontrado con <strong>la clase <a href="http://java.sun.com/javase/6/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>, esta clase permite la traducción a partir de un archivo tipo <em>properties</em></strong> (que se basa en líneas <code>campo = valor</code>).</p>
<p>Lo malo es que <strong>Java trata estos archivos <em>properties</em> utilizando la codificación ISO-8859-1, de modo que si utilizas un archivo <em>properties</em> guardado en UTF-8 algunos símbolos (por ejemplo: la eñe) se mostrarán de forma incorrecta.</strong> Para solucionar este problema, se debe usar el siguiente truco:</p>
<pre class="brush:java">import java.io.UnsupportedEncodingException;
import java.util.ResourceBundle;

public class UTFi18n {

    // Messages es el nombre del archivo que contiene los mensajes traducidos
    private ResourceBundle messages =  ResourceBundle.getBundle ("Messages");

    public static String getMessage (String message) {

        String value = messages.getString (message);
        String ret;

        try {
            // Hack to manage properties files using UTF-8
            ret = new String (value.getBytes ("ISO-8859-1"), "UTF-8");

        } catch (UnsupportedEncodingException encEx) {
            ret = value;
        }

        return ret;
    }
}</pre>
<p>Basado en <a href="http://www.thoughtsabout.net/blog/archives/000044.html">Quick and Dirty Hack for UTF-8 Support in ResourceBundle</a> de Thoughts About.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/internacionalizacion-en-java-usando-utf-8/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Los problemas de la clase JSObject</title>
		<link>http://blog.4bits.es/los-problemas-de-la-clase-jsobject/</link>
		<comments>http://blog.4bits.es/los-problemas-de-la-clase-jsobject/#comments</comments>
		<pubDate>Mon, 19 Oct 2009 07:56:46 +0000</pubDate>
		<dc:creator>Fran</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=339</guid>
		<description><![CDATA[Como los applets se ejecutan dentro de una página web, se creó un mecanismo (llamado LiveConnect) para comunicarse con el navegador mediante JavaScript, es decir, poder ejecutar JavaScript desde el applet. Este mecanismo ofrece una clase llamada JSObject que permite realizar la comunicación del applet a JavaScript, esta clase se localiza en el paquete netscape.javascript. [...]]]></description>
			<content:encoded><![CDATA[<p>Como los <em>applets</em> se ejecutan dentro de una página web, se creó un mecanismo (llamado <a href="http://en.wikipedia.org/wiki/LiveConnect">LiveConnect</a>) para comunicarse con el navegador mediante JavaScript, es decir, poder ejecutar JavaScript desde el <em>applet</em>.</p>
<p>Este mecanismo ofrece <strong>una clase llamada <code>JSObject</code> que permite realizar la comunicación del <em>applet</em> a JavaScript</strong>, esta clase se localiza en el paquete <code>netscape.javascript</code>.</p>
<p>Lo malo es que esta clase tiene varios problemas y fallos relacionados que no vienen documentados en ningún sitio, sólo en algunas páginas de gente que ha conseguido averiguarlos por su cuenta.</p>
<h3>JSObject no es <em>thread safe</em></h3>
<p>En los ejemplos de <a href="http://java.sun.com/j2se/1.4.2/docs/guide/plugin/developer_guide/java_js.html">la página de Sun dedicada a la clase JSObject</a>, se puede ver un ejemplo de su uso:</p>
<pre class="brush:java">import netscape.javascript.*;
import java.applet.*;
import java.awt.*;

class MyApplet extends Applet {
    public void init() {
        JSObject win = JSObject.getWindow (this);
        JSObject doc = (JSObject) win.getMember ("document");
        JSObject loc = (JSObject) doc.getMember ("location");

        // document.location.href
        String s = (String) loc.getMember ("href");
        win.call ("f", null);   // Call f() in HTML page
    }
}</pre>
<p>Bien, pues <strong>este uso no es correcto, ya que esta clase no es <em>thread safe</em></strong> y en las versiones más recientes del complemento de Java para los navegadores, puede que <strong>la máquina virtual de Java ejecute varios hilos y el uso de esta clase acabe fallando.</strong> Para arreglarlo, sólo se debe hacer lo siguiente:</p>
<pre class="brush:java">synchronized (JSObject.class) {
    win = JSObject.getWindow (applet);
}</pre>
<p>Básicamente, se trata de encapsular el uso de la clase <code>JSObject</code> en un bloque <code>synchronized</code> para evitar fallos por cualquier causa relacionada con hilos.</p>
<h3>JSObject.getMember () no funciona en Firefox sobre Mac OS X</h3>
<p>Otro problema que tiene esta clase es que <strong>en la versión de Firefox para Mac OS X, el método <code>getMember</code> no funciona bien y siempre devuelve <code>null</code></strong>, lo que obliga a usar un trozo de código algo feo. Por ejemplo, para obtener el <em>userAgent</em> del navegador:</p>
<pre class="brush:java">synchronized (JSObject.class) {
    win = JSObject.getWindow (applet);
}
JSObject nav = win.getMember ("navigator");
String userAgent = (String) nav.getMember ("userAgent");</pre>
<p>Utilizando el método <code>getMember</code>.</p>
<pre class="brush:java">synchronized (JSObject.class) {
    win = JSObject.getWindow (applet);
}
String userAgent = (String) win.eval ("navigator.userAgent");</pre>
<p>Un apaño, utilizando el método <code>eval<code>.</p>
<p>Basado en <a href="http://www.nakov.com/blog/index.php?s=jsobject">los posts sobre JSObject de Svetlin Nakov</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/los-problemas-de-la-clase-jsobject/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Applet-fu insertando fácilmente los applets en (X)HTML</title>
		<link>http://blog.4bits.es/applet-fu-insertando-facilmente-los-applets-en-xhtml/</link>
		<comments>http://blog.4bits.es/applet-fu-insertando-facilmente-los-applets-en-xhtml/#comments</comments>
		<pubDate>Thu, 01 Oct 2009 09:09:59 +0000</pubDate>
		<dc:creator>Fran</dc:creator>
				<category><![CDATA[(X)HTML]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Recursos]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=302</guid>
		<description><![CDATA[Sigo pesado con los applets, esta vez porque he descubierto un script de JavaScript (applet-fu) que permite insertar los applets en (X)HTML de manera sencilla y evitando tener que preocuparte si será compatible con IE o cumplirá los estándares (X)HTML. A costa de unos cuantos KB del script de JavaScript que hay que incluir en [...]]]></description>
			<content:encoded><![CDATA[<p>Sigo pesado con los <em>applets</em>, esta vez porque he descubierto <strong>un script de JavaScript (<a href="http://products.metamolecular.com/2009/06/08/better-applet-deployment-with-applet-fu">applet-fu</a>) que permite insertar los <em>applets</em> en (X)HTML de manera sencilla y evitando tener que preocuparte <a href="http://blog.4bits.es/applets-en-xhtml-1-1-compatibles-con-ie/">si será compatible con IE o cumplirá los estándares (X)HTML</a>.</strong></p>
<p>A costa de unos cuantos <abbr title="Kilobytes">KB</abbr> del script de JavaScript que hay que incluir en el (X)HTML, se pueden insertar <em>applets</em> con el siguiente código:</p>
<pre class="brush:html">&lt;script type="text/javascript" language="javascript" charset="utf-8"&gt;
    applet_fu.run (
        // Atributos del elemento (X)HTML (como object)
        { 'width'  : '0',
          'height' : '0',
          'name'   : 'applet',
          'id'     : 'applet' },
        // Parámetros del applet
        { 'archive' : 'applets/Applet.jar',
          'code' : 'es.4bits.Applet' },
        // Versión de Java necesaria
        '1.6',
        // Mensaje mostrado si no se encuentra la versión de Java
        '&lt;p&gt;No se ha encontrado la versión de Java necesaria.&lt;/p&gt;'
    );
&lt;/script&gt;</pre>
<p>Además, este código ofrece mediante el mecanismo del navegador utilizado la posibilidad de descargar Java si no se ha encontrado o si tiene una versión distinta a la necesaria.</p>
<p>Sun ofrece algo parecido llamado <a href="http://java.sun.com/javase/6/docs/technotes/guides/jweb/deployment_advice.html#deplToolkit">Java Deployment Kit</a>, pero ocupa más, sólo por realizar más tareas como la posibilidad de usar Java Web Start.</p>
<p>Dejo el enlace a su repositorio en github, para quién lo quiera descargar: <a href="http://github.com/metamolecular/applet-fu/tree/master">código de applet-fu</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/applet-fu-insertando-facilmente-los-applets-en-xhtml/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Applets firmados, JavaScript y la pérdida de privilegios</title>
		<link>http://blog.4bits.es/applets-firmados-javascript-y-la-perdida-de-privilegios/</link>
		<comments>http://blog.4bits.es/applets-firmados-javascript-y-la-perdida-de-privilegios/#comments</comments>
		<pubDate>Wed, 16 Sep 2009 08:07:01 +0000</pubDate>
		<dc:creator>Fran</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=286</guid>
		<description><![CDATA[De forma predeterminada, los applets de Java tienen sus funciones limitadas por la sandbox para que no puedan acceder a la máquina local. Pero si el applet se firma, y el usuario acepta su ejecución (mediante una ventana de aviso) el applet podrá acceder a la máquina local como si se tratase de una aplicación [...]]]></description>
			<content:encoded><![CDATA[<p>De forma predeterminada, los <em>applets</em> de Java tienen sus funciones limitadas por la <em>sandbox</em> para que no puedan acceder a la máquina local. Pero si el <em>applet</em> se firma, y el usuario acepta su ejecución (mediante una ventana de aviso) el <em>applet</em> podrá acceder a la máquina local como si se tratase de una aplicación Java más.</p>
<p>Hasta aquí todo bien, pero resulta que si desde <strong>la web en la que se carga el <em>applet</em> firmado, se realiza una llamada a cualquier método de éste desde JavaScript, el <em>applet</em> vuelve a perder los privilegios ya que el código JavaScript proviene es inseguro.</strong></p>
<p>Aquí surge la pregunta, ¿cómo se hace para poder ejecutar métodos de un <em>applet</em> firmado desde JavaScript sin que el <em>applet</em> pierda los privilegios? La respuesta es utilizar el siguiente trozo de código:</p>
<pre class="brush:java">AccessController.doPrivileged (
    new PrivilegedAction () {
        public Object run () {
            // Código con privilegios
        }
    });</pre>
<p>Este código permite ejecutar el código que se quiera con privilegios, además el valor devuelto es de tipo <code>Object</code>, por lo que se puede devolver el tipo de objeto que se quiera.</p>
<p>Basado en <a href="http://www.gnegg.ch/2009/04/javascript-and-applet-interaction/">JavaScript and Applet interaction</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/applets-firmados-javascript-y-la-perdida-de-privilegios/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>compareTo vs equals</title>
		<link>http://blog.4bits.es/compareto-vs-equals/</link>
		<comments>http://blog.4bits.es/compareto-vs-equals/#comments</comments>
		<pubDate>Fri, 20 Mar 2009 18:57:12 +0000</pubDate>
		<dc:creator>Lek</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=176</guid>
		<description><![CDATA[Al actualizar el código de un proyecto antiguo uno se puede encontrar cualquier cosa. En una de esas paranoias mías (o eso creía yo) me dio por dedicarme a cambiar todos los compareTo por equals, que me parecía como una instrucción más directa, óptima y mejor. No iba yo muy desencaminado. La primera nos devuelve [...]]]></description>
			<content:encoded><![CDATA[<p>Al actualizar el código de un proyecto antiguo uno se puede encontrar cualquier cosa. En una de esas paranoias mías (o eso creía yo) me dio por dedicarme a cambiar todos los <code>compareTo</code> por <code>equals</code>, que me parecía como una instrucción más directa, óptima y mejor. <strong>No iba yo muy desencaminado</strong>.</p>
<p>La primera nos devuelve un entero que indica si el objeto es mayor que el parámetro de entrada, menor, o igual. La segunda nos devuelve directamente un <code>booleano</code> que nos dice si son iguales o no. Pero yo personalmente no recuerdo haber utilizado nunca el <code>compareTo</code> para algo que no fuera comprobar si un objeto (y más en concreto un <code>String</code>) es igual a otro. Así pues, me he creado un código chorras para saber cuánta diferencia hay (de paso metí el <code>equalsIgnoreCase</code>):</p>
<pre class="brush:java">public static void main (String [] args) {
  String s = "necesito una prueba a ver si cuela ésta misma";
  long ini = System.nanoTime ();
  for (int i = 0 ; i < 10000000; i ++ ) {
    boolean b = s.compareTo ("necesito una prueba") == 0;
  }
  System.out.println (
      "CompareTo: " + (System.nanoTime () - ini)/1000000.0);

  ini = System.nanoTime ();
  for (int i = 0 ; i < 10000000; i ++ ) {
    boolean b = s.equals ("necesito una prueba");
  }
  System.out.println (
      "equals: " + (System.nanoTime () - ini)/1000000.0);

  ini = System.nanoTime ();
  for (int i = 0 ; i < 10000000; i ++ ) {
    boolean b = s.equalsIgnoreCase ("necesito una prueba");
  }
  System.out.println (
      "equalsIgnoreCase: " + (System.nanoTime () - ini) /1000000.0);
}
</pre>
<p>Los resultados son demoledores. <strong><code>compareTo</code> es unas 150 veces más lento que <code>equals</code></strong>:</p>
<blockquote><p>(el tiempo va en milisegundos)<br />
CompareTo: 1886.853143<br />
equals: 71.728111<br />
equalsIgnoreCase: 60.81111</p></blockquote>
<p>Sorprende que <code>equalsIgnoreCase</code> sea más rápido que <code>equals</code>, sobre todo porque lo primero que hace es ejecutar el <code>equals</code>. Puede ser cosa del orden, pero probando a cambiarlas los datos no variaban demasiado. Sí puedo decir que de tanto en tanto <code>equals</code> es más rápido. En cualquier caso, puede parecer una chorrada <strong>perder 1'8 segundos en 10 millones de consultas</strong> que no se van a dar así en un programa real, pero al final todo suma y si los <code>Strings</code> ya son poco óptimos de por sí...</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/compareto-vs-equals/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Cuidado con la multiplataforma</title>
		<link>http://blog.4bits.es/java-multiplataforma/</link>
		<comments>http://blog.4bits.es/java-multiplataforma/#comments</comments>
		<pubDate>Mon, 22 Sep 2008 17:59:25 +0000</pubDate>
		<dc:creator>Lek</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=89</guid>
		<description><![CDATA[Una de las cosas que se nos vende con Java es que al depender de una máquina virtual tu código es independiente de la plataforma sobre la que se ejecute. Esta afirmación se cumple para la gran mayoría de casos (especialmente si eres un buen programador y utilizas los menores literales posibles), pero no es [...]]]></description>
			<content:encoded><![CDATA[<p>Una de las cosas que se nos vende con <strong>Java</strong> es que al depender de una máquina virtual <strong>tu código es independiente de la plataforma</strong> sobre la que se ejecute. Esta afirmación se cumple para la gran mayoría de casos (especialmente si eres un buen programador y utilizas los menores literales posibles), pero <strong>no es una tautología</strong>. Es como decir que la tierra es esférica. Casi, pero no.</p>
<p>Una cosa muy curiosa que me ha pasado hoy y me ha tenido entretenido la mitad del día va relacionada con esto. Para ciertas tareas Java se apoya en librerías nativas del sistema operativo. Y con los nativos hemos topado. Imaginad que os habéis currado una aplicación que os muestra una serie de gráficos. Depurais, probais, mostrais al cliente y, por fin, pasais a producción. Aquí se produce la hecatombe. Error Java chungo:</p>
<blockquote><p>Can&#8217;t connect to X11 window server using &#8216;:0.0&#8242; as the value of the DISPLAY variable.</p></blockquote>
<p>Situación: Servidor web Tomcat más viejo que <em>la tarara</em> montado sobre un Linux más viejo que <em>el Tomcat más viejo que la tarara</em>. A pesar de que me ha llevado bastante decidirme a buscarlo en Google pensando que sería un tema de librerías, al parecer es un problema bastante común en librerías no-PJA (Puro Java AWT), como <a href="http://www.jfree.org/jfreechart/">jFreeChart</a>, cuando el equipo donde se está ejecutando la aplicación no tiene un servidor gráfico rulando. <strong>Si fueran librerías Java puras no pasaría nada</strong>.</p>
<p>La solución es tan sencilla como lo que sigue como añadir la siguiente línea en el <code>catalina.sh</code>:</p>
<blockquote><p>CATALINA_OPTS=&#8221;-Djava.awt.headless=true&#8221;</p></blockquote>
<p>El motivo es que las funciones gráficas necesitan una ventana X ejecutándose. Si no la hay, el comando anterior le dice a la ejecución que utilice buffers virtuales (<a href="http://forums.sun.com/thread.jspa?messageID=693085">Fuente</a>).</p>
<p>Y ahora, la pregunta. Si el programa instalado era una actualización, ¿cómo funcionaba antes?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/java-multiplataforma/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Analizando código con Eclipse</title>
		<link>http://blog.4bits.es/analizando-codigo-con-eclipse/</link>
		<comments>http://blog.4bits.es/analizando-codigo-con-eclipse/#comments</comments>
		<pubDate>Tue, 15 Jul 2008 16:32:14 +0000</pubDate>
		<dc:creator>Lek</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Recursos]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=65</guid>
		<description><![CDATA[Una de las cosas que hacen grande al IDE Eclipse (que recientemente ha estrenado versión, la 3.4) es la cantidad de plugins que uno encuentra para casi cualquier cosa. Uno de esos tipos de plugin, de los más útiles, son los de herramientas de revisión de código. Si nunca habéis usado uno y tenéis la [...]]]></description>
			<content:encoded><![CDATA[<p>Una de las cosas que hacen grande al IDE Eclipse (que recientemente ha estrenado versión, la 3.4) es la cantidad de plugins que uno encuentra para casi cualquier cosa. Uno de esos tipos de plugin, de los más útiles, son los de herramientas de revisión de código. Si nunca habéis usado uno y tenéis la moral baja, no los uséis ahora; <strong>la primera vez siempre sonroja un tanto</strong>. En mi caso, he utilizado 3 hasta la salida del nuevo Eclipse. La mayor parte de lo que viene a continuación lo encontré después de aquel post <a href="http://blog.4bits.es/plugins-eclipse/">Plugins de Eclipse</a>, así que vamos con ellos:</p>
<p><strong><a href="http://findbugs.sourceforge.net/">FindBugs</a></strong></p>
<p>Éste estaba en la lista anterior. Es un analizador de código bastante eficiente en lo suyo (bugs potenciales y <em>algunas</em> malas prácticas de programación) marcando todo lo que encuentre en 3 niveles de gravedad. Personalmente es mi favorito, sobre todo para empezar, pues no me resulta tan paranoico como los demás y puedes corregir casi todo lo que marca.</p>
<p><strong><a href="http://pmd.sourceforge.net/">PMD</a></strong></p>
<p>Más completo que el anterior (mucho más), además de todo lo anterior, también comprueba la complejidad o longitud del código. Pero <strong>resulta un paranoico</strong> que ofrece pocas soluciones reales. Por ejemplo, te dice que una variable se puede declarar como final y cuando la declaras te dice que no, que no se deben declarar variables finales, que uses campos. ¿¿¿¡¡En qué quedamos!!???</p>
<p>A su favor tiene que es mucho más completo y que también permite buscar código duplicado (malditos <em>copypastes</em>).</p>
<p><strong><a href="http://www.enerjy.com/index.html">Enerjy</a></strong></p>
<p>Me enteré de este plugin a través de <a href="http://www.javahispano.org/contenidos.item.action?id=6687690&#038;menuId=NEWS">JavaHispano</a>, debido a su reciente gratuidad. Es similar al PMD, tiene algunas reglas más paranoicas todavía (uso de tabulador en lugar de espacios) y tiene la grave, gravísima desventaja de que <strong>no se ejecuta bajo demanda</strong>. Aunque puedes marcar qué proyectos excluir del análisis, me parece un tanto inviable en <em>workspaces</em> con muchos proyectos. Una ventaja que le encuentro es que te cambia automáticamente los bucles <code>for</code> de Java 1.4 por los de Java 5 (donde es posible, claro), aunque tampoco estaría mal que lo hiciera sólo si estás trabajando con Java 5 ó superior :-/</p>
<p><strong>Fin</strong></p>
<p>Esta última fiebre <em>revisionista</em> me dio tras leer <a href="http://jcesarperez.blogspot.com/2008/03/revisiones-de-cdigo-automatizadas-con.html">Revisiones de código automatizadas con Eclipse</a>, donde se habla más extensamente de los 2 primeros y también del <a href="http://checkstyle.sourceforge.net/">CheckStyle</a>. Éste último lo instalé, me empezó a señalar que las variables no cumplían con el estándar de nombrado y, con la misma, lo envié al limbo. Paranoias vale, pero las justas; una variable <code>boolean</code> que no se llame <em>sexo</em> no es seria ;)</p>
<p>¿Habéis utilizado alguno de estos plugins? ¿Quizá otro que desconozca? ¿Qué ventajas y desventajas veis a estas herramientas?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/analizando-codigo-con-eclipse/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JavaCup 2008</title>
		<link>http://blog.4bits.es/javacup-2008/</link>
		<comments>http://blog.4bits.es/javacup-2008/#comments</comments>
		<pubDate>Fri, 02 May 2008 12:30:53 +0000</pubDate>
		<dc:creator>Lek</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Noticias]]></category>

		<guid isPermaLink="false">http://blog.4bits.es/?p=56</guid>
		<description><![CDATA[¿Qué programador futbolero no ha soñado nunca con hacerse su propio PCFútbol? Pues a alguien se le ocurrió en su día que podía ser una buena idea programar equipos de fútbol y que jugaran entre sí una suerte de copa en busca de la gloria, la fama y la satisfacción de hacer morder el polvo [...]]]></description>
			<content:encoded><![CDATA[<p>¿Qué programador futbolero no ha soñado nunca con hacerse su propio PCFútbol? Pues a alguien se le ocurrió en su día que podía ser una buena idea programar equipos de fútbol y que jugaran entre sí una suerte de copa en busca de la gloria, la fama y la satisfacción de hacer morder el polvo a esos engreídos de los contrarios&#8230; pues eso es la <a href="http://javacup.javahispano.org">JavaCup 2008</a>, que desde la asociación JavaHispano <a href="http://weblogs.javahispano.org/jh/entry/ayudarnos_a_promocionar_la_javacup">piden ayuda para publicitarla</a>.</p>
<blockquote><p><em>La revista Sólo Programadores, la organización sin ánimo de lucro javaHispano y Sun Microsystems hemos organizado un concurso que consiste en un torneo virtual de fútbol donde cada equipo será una clase Java que implementa una interfaz predefinida.</em></p></blockquote>
<p>La verdad es que suena muy interesante, ¿no creéis?, y hasta hay premio monetario (ah, el vil metal). ¿A qué esperáis para programar a vuestros gladiadores futboleros?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.4bits.es/javacup-2008/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
