22 diciembre 2005

Patrón Adaptador

Hemos decidido hacernos un programa de agenda telefónica en ordenador, en java. Como somos bastante hábiles, nos olvidamos de todo el tema de ventanas y decidimos hacer primero el código relativo a la gestión de los datos.

En primer lugar nos hacemos una clase Amigo con varios atributos: nombre, apellido, teléfono, dirección, e-mail, etc, etc. Luego nos hacemos una clase ListaAmigos que mantiene la lista de amigos y tiene un montón de métodos como añadeAmigo(), borraAmigo(), dameAmigo(int), dimeCuantosAmigosTengo(), etc, etc.

Ya está todo estupendo. Nuestra clase ListaAmigos funciona perfectamente. Además hemos hecho todo el código para guardar y sacar la ListaAmigos de una base de datos.

Ahora nos decidimos a hacer las ventanitas. En java lo que más nos gusta para meter la ListaAmigos es un JTable, una especie de tabla con filas y columnas. En cada fila pondremos a un Amigo y en cada columna uno de sus datos: nombre, apellido, etc.

Nos ponemos con el JTable y resulta que el JTable admite como datos un TableModel. Un TableModel es una clase que entre otros tiene los métodos getColumnCount(), para saber el número de columnas de la tabla; getRowCount(), para saber el número de filas de la tabla y getValueAt(fila,columna) para saber el dato de una determinada fila y columna.

¡Vaya por Dios!. Nuestra ListaAmigos no se parece en nada al TableModel, no podemos meterla directamente en un JTable. La pena es que realmente sí tiene un parecido, el getRowCount() es similar al dimeCuantosAmigosTengo(), el getColumnCount() debería devolver el número de atributos de la clase Amigo y el getValueAt(fila,columna) debería recoger el dameAmigo(fila) y luego devolver un atributo del Amigo obtenido.

Como no nos apetece deshacer todo lo de ListaAmigos para que se parezca a un TableModel, echamos mano del patrón Adaptador. Con este patrón tenemos que hacer una clase AdaptadorListaAmigosATableModel que tenga dentro un ListaAmigos como atributo y que implemente la interface TableModel

public class AdaptadorListaAmigosATableModel implements TableModel
{
private ListaAmigos listaAmigos;
public AdaptadorListaAmigosATableModel (ListaAmigos listaAmigos)
{
this.listaAmigos=listaAmigos;
}
...
}

y ahora sólo hay que implementar los métodos de TableModel haciendo las llamadas equivalentes en ListaAmigos

public int getColumnCount()
{
return 6; // Suponiendo que Amigo tiene 6 atributos
}

public int getRowCount()
{
return listaAmigos.dimeCuantosAmigosTengo();
}

public Object getValueAt (int fila, int columna)
{
Amigo amigo = listaAmigos.dameAmigo(fila);
switch (columna)
{
case 0: return Amigo.nombre;
caso 1: return Amigo.apellido;
...
}
}

Listo. Nos basta con hacer esto

AdaptadorListaAmigosATableModel adaptador = new AdaptadorListaAmigosATableModel(listaAmigos);
JTable tabla= new JTable(adaptador);

para tener metida nuestra ListaAmigos en un JTable.

Por supuesto, TableModel lleva más métodos que también debemos implementar...

21 diciembre 2005

Patrón Singleton

Supongamos que estamos en un proyecto con varias personas. Nuestra aplicación se conecta con un socket con otro programa que corre en algún lado. Alguien de nuestro proyecto hace la clase Conexion que dentro establece la conexión a través de socket con el otro programa. A esta clase se le pone los métodos enviaMensaje() y un avisameCuandoLLegueMensaje(), de forma que todo el mundo pueda enviar mensajes por la conexión y ser avisado cuando llegue algún mensaje en el que tenga interés.

Todos los que necesitan esta Conexion deben poner en sus clases un método tomaConexion() que reciba como parámetro la Conexion, y guardarsela para enviar mensajes o recibirlos.

Además, como es habitual, la documentación de diseño no es lo suficientemente buena y alguno de nuestros programadores despistados, se olvida de poner el método y decide simplemente hacer un new Conexion().

Por un lado tenemos el rollo de andar pasando la Conexion a todo el mundo. Por otro lado, nuestro programa misteriosamente funciona mal porque hay varias clases Conexion instanciadas y el otro programa piensa que somos varios clientes y la mensajería que recibimos no es del todo coherente (eso si no tenemos directamente problemas con la conexión al haber varios peleándose por la misma).

El patrón Singleton nos ayuda a que nuestra instancia Conexion sea única, a la vez que la hace accesible a todo el mundo sin necesidad de andar pasándola.

Para ello debemos hacer varias cosas:
  • La clase Conexion debe tener un constructor privado, de forma que nadie, salvo ella misma, pueda instanciarla.
  • La clase Conexion debe tener un atributo estático privado, inicialmente a null. En él se guardará la única instancia de esta clase.
  • La clase Conexion debe tener un método estático público dameConexion(). Este método crea una instancia de Conexion la primera vez que se le llama y la guarda en el atributo estatico. Luego devuelve dicho atributo estático en el return.
Puede ser algo como esto

public class Conexion
{

private Conexion()
{
...
}

private static Conexion laConexion = null;

public Conexion dameConexion()
{
if (laConexion == null)
laConexion = new Conexion();
return laConexion;
}

}

Cuando alguien necesita una conexion, simplemente debe hacer

Conexion miConexion = Conexion.dameConexion();

y tiene accesible la única instancia de Conexion que hay.

17 diciembre 2005

Patrón factoria

Otro patrón más, el patrón factoria.

Supongamos que vamos a hacer una aplicación y en ella hay que pedir en varias ventanas (pongamos en unas 500 ventanas, para ser bestias) una fecha. Por ejemplo, si estamos haciendo una especie de agenda con listín de amigos, debemos poner fechas de las citas, fechas de los cumpleaños, fecha para alarmas, aniversarios, eventos, etc, etc.

Como somos muy hábiles programando, nos hacemos una clase EditorFecha. Además, como programamos en java, esa clase EditorFecha en un JTextField en el que se pone la fecha y le hemos añadido unos métodos dameFecha() y tomaFecha() que nos devuelven o admiten directamente un Date. De esta manera nos ahorramos la traducción de String a Date en todos los sitios.

Hacemos nuestra aplicación y en el panel/formulario que nos permite crear un nuevo amigo ponemos un "new EditorFecha()". En el panel/formulario que nos permite editar un nuevo amigo ponemos otro "new EditorFecha()". En el que nos permite añadir una nueva cita ponemos otro "new EditorFecha()", etc, etc. Así en las 500 ventanas que mencionamos al principio hacemos nuestros 500 "new EditorFecha()".

Ya contentos con nuestra aplicación que funciona estupendamente se la enseñamos a nuestro mejor amigo (habitualmente nuestro jefe que nos la ha encargado). Nuestro amigo nos dice que la aplicación es estupenda, pero que para meter la fecha es más cómodo usar la clase JCalendar que te puedes bajar gratis de internet. Esa clase tiene un botoncito que cuando le damos nos abre un calendario en el que elegimos la fecha con un click de ratón y ya está.

Nos parece buena idea, nos bajamos JCalendar y nos ponemos a cambiar código. ¡¡ Ay desgraciados de nosotros !!. Cuando hemos cambiado 133 "new EditorFecha()" por 133 "new JCalendar()" y hemos cambiado 133 "dameFecha()" por 133 "getDate()" nos hemos hartado, mandamos todo a freir espárragos. Y hemos sido demasiado pacientes, hemos llegado a cambiar 133 editores.

¿Cómo hubiera sido la forma correcta de hacer esto para que el cambio no nos suponga semejante trauma?. El patrón factoría nos da una posible solución.

Cuando preveamos que una clase puede cambiarse por otra o mejorarse (aunque sea remotamente), debemos hacerlo de la siguiente manera.

Primero hacemos una interface para esa clase con los métodos que queramos que tenga. Por ejemplo, nuestra InterfaceEditorFecha puede ser como esto

public interface InterfaceEditorFecha
{
public Date dameFecha();
publid void tomaFecha(Date unaFecha);
}

Ahora hacemos nuestro EditorFecha implementando esa interface.

Finalmente, hacemos una clase factoría con un método estático que nos de un editor nuevo cada vez que la llamemos y nos lo devuelva como esa interface. Puede ser algo como esto

public class FactoriaEditorFecha
{
public InterfaceEditorFecha dameNuevoEditor ()
{
return new EditorFecha();
}
}

Ya está. En nuestro código, en vez de hacer "new EditorFecha()", hacemos "FactoriaEditorFecha.dameNuevoEditor()" para ir construyendo nuestros editores.

Ahora viene el "amigo" del JCalendar y nos da su sabio consejo. Hacemos una clase que herede de JCalendar y que implemente nuestra interface. Los dos métodos que queremos que tenga únicamente deben hacer la traducción a los de JCalendar. Para esto existe otra cosa que se llama patrón adaptador, pero ya escribiré más adelante sobre él.

public class MiJCalendar extends JCalendar implements InterfaceEditorFecha
{

public void tomaFecha (Date unaFecha)
{
this.setDate(unaFecha);
}

public Date dameFecha ()
{
return this.getDate();
}

}

y simplemente cambiando en nuestra clase FactoriaEditorFecha el "new EditorFecha()" por "new MiJCalendar()" ya tenemos TODA la aplicación cambiada.

En realidad el patrón factoría es algo más complejo y permite incluso cambiar el new que hace sin necesidad de tocar el código de la clase Factoría, pero ya me parece liar mucho para un primer vistazo.

Una cosa importante, JCalendar existe, pero me he inventado sus métodos por pereza y por simplificar un poco.

16 diciembre 2005

¿Qué son los patrones de diseño?. El patrón Observador

Llevo algún tiempo usando patrones de diseño y la verdad, es algo interesante y que merece la pena aprender cuando se anda metido en proyectos de programación un poco en serio.

Uno de los principales problemas que veo con todo este tema de patrones de diseño es que no hay ningún sitio que explique de forma clara qué es un patrón de diseño a una persona que NO tenga ni idea de lo que es un patrón de diseño. Las definiciones que se dan de un patrón son demasiado abstractas y utilizan palabrarería demasiado formal. El resultado es que si no sabes lo que es un patrón de diseño, puedes interpretar esas definiciones como te de la gana.

Por ello pretendo dar aquí, más que una definición, una explicación informal de forma que alguien que no sepa qué es un patrón de diseño sepa, al menos, de qué va. Lo haré con un ejemplo.

Normalmente cuando nos dedicamos a codificar hay pequeños trozos de código que hacemos, a veces sin pensar en ellos porque nos resultan evidentes, otras veces estrujándonos los sesos porque no son tan evidentes. Si ese código sólo nos sirve para ese programa, todo va más o menos bien.

Sin embargo, es normal que luego tengamos que hacer otro programa similar. Es bastante normal también que tengamos que copiar el primer programa o trozos del mismo en otro sitio y empezar a modificar, porque el código que tenemos que hacer se parece al que teníamos, pero no es exactamente igual.

Cuando un programador se ve obligado a hacer eso una y otra vez, a copiar y pegar el mismo código y modificarlo una y otra vez, empieza a pensar en la forma de hacer ese trozo de código de forma que la tarea de llevárselo a otro sitio sea más cómoda para él. El programador empieza a inventar diferentes formas de hacer ese trozo de código de forma que sea lo suficientemente versátil y configurable como para no tener que volver a tocarlo nunca más. Idealmente, le debería bastar con llevarse la librería ya compilada de un programa a otro.

Ha habido en la historia miles de programadores con este problema y miles de ellos han llegado a las mismas soluciones o formas de hacer esos trozos de código de forma que sean completamente reutilizables. Esas soluciones son los "patrones de diseño" y en su día "la banda de los cuatro" (cuatro gurús de la programación) los recopilaron en un libro.

Los patrones de diseño nos cuentan cómo hacer nuestro código en esas pequeñas cosas, de forma que nuestro código sea reutilizable, se pueda cambiar cómo se hacen las cosas, ampliar su funcionalidad ... ¡¡ Sin necesidad de tocar ese código !!.

Vamos con un ejemplo para ver qué quiero decir. Supongamos que en nuestro programa nos llega un dato de algún sitio (pongamos un número, un 3) y debemos guardarnos ese dato junto con otros que nos llegaron anteriormente, pintarlos en un gráfico, mostrarlos en una ventana que tiene una lista de datos y además imprimirlo.

Nos ponemos a ello. Nos hacemos nuestro array/lista para añadir el dato junto a otros que nos han llegado anteriormente. Luego nos hacemos unas clases grafico, ventanaConLista e impresora a las que ponemos el método tomaNuevoDato(int) para que hagan lo que tiene que hacer con el dato.

El código nos puede quedar más o menos así (supongamos que dato es el nuevo dato que nos ha llegado).

array[i] = dato;
grafico.tomaNuevoDato(dato);
ventanaConLista.tomaNuevoDato(dato);
impresora.tomaNuevoDato(dato);

Bien, todo esto está estupendo y parece correcto. Además, no hemos tenido que pensar demasiado en cómo repartir el dato a los distintos sitios. Si nadie nos pide modificar el código o extraer un trozo de él para usarlo en otro sitio, todo está bien.

Vamos ahora a empezar a sacarle pegas a este código.


Para pasar el dato a alguien más, hay que añadir líneas a este código.

Si ahora nos piden que el dato se envíe también a través de un socket, tenemos que hacernos nuestra clase MiSocket, ponerle el método tomaNuevoDato() y llamarlo. En el código anterior, hay que añadir una nueva línea.

miSocket.tomaNuevoDato(dato);

Estaría mucho mejor si este código ya hecho NO tuvieramos que editarlo, recompilarlo y quizás incluso depurarlo con debugger.

Este trozo de código depende de muchas cosas

Este trozo de código (y la clase que lo contiene) debe ver a las clases Grafico, VentanaConLista, Impresora y ahora MiSocket. Este código (ni esta clase) no funciona si no se dispone de todas las demás clase. Como además contiene el array de datos, cualquier otro que necesite el array de datos y se lo pida a esta clase, también necesita ver (de forma indirecta) a esas clases de Grafico, VentanaConLista..., etc.


Todos estos problemas no son problemas para la gente que empieza a programar o hace proyectos totalmente distintos unos de otros. Este problema lo encuentra la gente que luego hace un segundo proyecto muy similar, tiene que copiar este código, editarlo, quitar por ejemplo el gráfico porque en su siguiente proyecto no hay gráfico y añadir un Plotter (ahora quieren pintar el dato que llega en un Plotter).

Los patrones de diseño nos enseñan una solución al problema de repartir el dato a varios sitios, de forma que el código que reparte el dato NO hay que cambiarlo. El patrón de diseño que nos permite hacer esto se llama Observador (Observer pa los extranjeros).

¿Cómo se hace eso con el patrón Observador?. El patrón Observador nos dice que hagamos una interface con el métdo tomaNuevoDato(), así

public interface ObservadorNuevoDato
{
public void tomaNuevoDato(int nuevoDato);
}

Nos dice luego, que nuestras clases Grafico, VentanaConLista, etc implementen esa interface (cosas que ya están haciendo, puesto que tienen el método tomaNuevoDato()).

public class Grafico implements ObservadorNuevoDato
{
public void tomaNuevoDato (int nuevoDato)
{
this.pintoElDato(nuevoDato);
}
}

y ahora viene lo realmente interesante. Debemos hacernos una clase que tenga dentro el array de datos, que tenga una lista de ObservadorNuevoDato y que admita añadir y borrar ObservadorNuevoDato de esa lista. Cada vez que llegue un nuevo dato, debe recorrer la lista de Observadores y avisarles. Algo como esto

public class MisDatos
{
protected int [] array;
protected LinkedList observadores;
public MisDatos()
{
...
}
public void anhadeObservador(ObservadorNuevoDato observador)
{
observadores.add(observador);
}
public void tomaNuevoDato (int nuevoDato)
{
array[i] = nuevoDato;
for (int j=0;j<observadores.size();j++)
((ObservadorNuevoDato)observadores.get(j)).tomaNuevoDato(nuevoDato);
}
public void borraObservador (...)
{
...
}
}

Bueno, esto es lo mismo que teníamos pero más complicado. Sin embargo nos ahorra los dos problemas anteriores.

Esta clase NO es necesario volver a tocarla. Admite que le pongamos Observadores a nuestro gusto.

Esta clase NO depende de nadie (sólo de la interface). Podemos llevarnos la clase y la interface a cualquier otro proyecto sin necesidad siquiera de recompilarla, nos podemos llevar el .jar donde esté sin necesidad de llevarnos el fuente.

Obviamente, esta ventajas son mucho más ventajosas si los datos son algo más complicado que un array de enteros y la clase es algo más que una clase con un array dentro...

10 diciembre 2005

Una pequeña historia web

No pretendo aquí dar una lista detallada de todas las tecnologías web ni tampoco describirlas exhaustivamente. Es simplemente una pequeña historia de cómo han evolucionado y en qué consisten, así, por encima.

Inicialmente el asunto era bastante sencillo. En un ordenador, el servidor, había un montón de ficheros html y un programa, llamado servidor web. Los ficheros html son ficheros de texto con un formato espcial que permite varios tipos de fuente de letra, negritas, colores, cursivas, hacer tablas, etc, etc. Otro programa, nuestro navegador, cuando ponemos "http://www.geocities.com/chuidiang/index.html" en él, se conecta con el servidor web, pide el fichero index.html, el servidor web nos lo envía y nuestro navegador lo muestra. Si pinchas el enlace verás un fichero .html pintado en tu navegador.

Hasta aquí todo perfecto. En algún momento alguien pensó que las páginas html eran demasiado estáticas, no permitian ningún tipo de interacción con el que las estaba mirando y que no eran demasiado vistosas. Aparecieron entonces algunos lenguajes de script, como javascript, flash, applets de java, etc. Estos son unos lenguajes de programación. Dentro del fichero html se pone una marca especial y a partir de esa marca va código de programación en el lenguaje elegido (javascritp, flash, etc). Los navegadores, cuando llegan a esa zona del fichero no sabe qué hacer con ella, ya que no es html estandard. Para entenderlo los navegadores necesitan un pequeño añadido, una extensión, llamada plug-in de javascritp, de flash, etc. Añadiendo estos plug-in (uno para cada lenguaje de programación que queremos que se entienda), el navegador es capaz de entender y ejecutar este lenguaje de programación, mostrandonos una página mucho más alegre.

En http://sestud.uv.es/manual.esp/jscript/listado.htm tienes un pequeño ejemplo de javascript. Al pasar el ratón sobre el menú, verás un triángulo que se mueve sobre la opción seleccionada. En http://www.geocities.com/chuidiang2/timer/ejemplotimer.html puedes ver unos applets de java, que simulan un reloj. Necesitas tener en ambos casos instalado un añadido que permita verlos.

Estos lenguajes de programación tienen una pega. Como se ejecutan en el navegador de la persona que pide las páginas web, están muy restringidos, hay muchas cosas que no pueden hacer. Por ejemplo, no tienen acceso al disco duro del ordenador del navegante. ¿Por qué?. Yo podría hacer un applet de java que borre el disco duro y meterlo en una página web. Bastaría que alguien visitara la página web para encontrarse la sorpresa de que se queda sin disco duro. Los navegadores prohiben a esots lenguajes el acceso a los recursos (disco duro, impresoras, etc) del sistema, incluso para lectura.

Imaginemos ahora que una compañía de trenes quiere hacer una página web con los horarios de los trenes. Con lo comentado hasta ahora, sólo puede hacer un montón de páginas html con todos los horarios. La primera debería ser una especie de lista de todas las estaciones de origen. Cada estación tendría un enlace a otra página en la que esté la lista de posibles destinos desde ese origen. Esta última página de destinos tendría un enlace a otra página en la que aparezcan horarios y precios para ir desde el origen al destino. Hacer todo esto y lo que es peor, mantenerlo o modificarlo, puede ser una verdadera pesadilla para el informático de esa empresa de trenes.

Para facilitar la creación y mantenimiento de páginas web de este estilo tan complejo, que permitan consultas a los usuarios y que menejen montones de datos que pueden ser cambiados (y cuyo sitio ideal de almacenaje es una base de datos), aparecen otro tipo de lenguajes, como php, asp o los servlets de java.

Con estos lenguajes, las páginas web ya no tienen extensión html, sino que tienen su propia extensión (.asp, .jsp, .php, etc). Estas páginas siguen siendo como siempre, internamente tienen formato html, pero en algunas partes de la página se pone una marca especial y se empieza a programar en lenguaje php, java (para las .jsp), etc. De momento es igual que los lenguajes de script como javascript y flash, pero hay una diferencia importante.

En primer lugar, el servidor necesita tener instalada una extensión que entienda ese lenguaje de programación. Antes la extensión iba en el navegador. Ahora va en el servidor. Cuando el navegador pide una página php (por ejemplo), el servidor web pasa la petición a la extensión de php. Este extensión convierte la página php en página html normal, ejecutando el código php y reemplazandolo por su salida de texto. Por ejemplo, si en una pagina php hay una linea que pone

?php echo "hola mundo"; ?

la extensión de php quitará esto y pondrá "hola mundo" en su lugar. El resultado será una página html estandard que se devolverá al servidor web y este a su vez al navegador.

Una gran ventaja es que el plug-in va instalado en el servidor. Los navegadores no necesitan nada especial para ver páginas php, asp o servlets java. La otra gran ventaja es que este código está hecho por gente que mantiene el servidor web y se ejecuta en el servidor web, por lo que no es necesario darle ninguna restricción. Estos lenguajes pueden en su servidor acceder a bases de datos, guardar información en fichereros, etc, etc. En http://members.lycos.co.uk/chuidiang/enlaces.php tienes una página php que consulta una base de datos de enlaces y la muestra en pantalla, junto con la información asociada a cada enlace.

En el caso concreto de las páginas jsp de java, se ha ido un poco más allá todavía. En las páginas jsp (igual que php o asp), es necesario mezclar página html tradicional con código de lenguaje de programación. La gente de las páginas jsp pensó que no es fácil encontrar personas que a la vez tengan buen gusto estético para diseñar páginas web (la parte html) y sean buenos programadores (la parte de java de la página jsp). Por ello decicieron separar un poco ambas tareas.

Nacen así las taglibs. Las taglibs son librerías de código java que hace determinadas cosas, pero que se pueden luego añadir a una página jsp de una forma similar a como se añaden los elementos normales de html. De esta forma, el programador java hace sus librerías de código y el diseñador de páginas web las usa de una forma similar a como hace el resto de la página.

La ejecución de estos lenguajes en el servidor en vez de en el navegador presenta un nuevo problema. Si un servidor de páginas php o jsp tiene mucho exito, su número de visitas se incrementa, está ejecutando código de un montón de clientes a la vez. Según va teniendo éxito la página, se va necesitando un servidor más potente, incluso varios servidores trabajando en paralelo.

Para mejorar la eficiencia de la ejecución en el servidor y facilitar la tarea de cambiar de servidor a otro/s más potentes, nace todo el tema de j2ee y ejbs.

Con j2ee además del servidor web, se necesita, asociado a él o independiente, un nuevo servidor llamado servidor de aplicaciones. El programador de la página únicamente hace trozos de código de su negocio concreto, los ejb. Por ejemplo, en nuestro ejemplo de la compañía de trenes, el programador de esa compañía únicamente hace clases java estilo "EstacionOrigen", "EstacionDestino", "Horario", etc. Es decir, clases java propias de su problema. En ningún caso necesita hacer clases de comunicaciones, de almacenaje o consulta en base de datos, etc, etc.

Una vez hechas y compiladas estas clases, se configura el servidor de aplicaciones para que sepa dónde están y cómo se guardan en base de datos. El servidor de aplicaciones se encarga el solito de crearlas, modificarlas, consultarlas y borrarlas en base de datos a petición del cliente. Si el servidor de aplicaciones dispone de varios ordenadores para su ejecución, incluso es capaz de ejecutar código repartiendo la carga entre ellos.

De esta forma, el programador sólo se preocupa de su problema. El servidor de aplicaciones se preocupa de dónde ejecutar las cosas y de cuando y como se guardan o modifican estas cosas en base de datos.

08 diciembre 2005

Más sobre j2ee

Después de pasarme un par de días urgando por internet, he llegado a una conclusión que todavía no sé si es correcta del todo. En ningún sitio lo pone claramente, pero me temo que sí lo es.

Apache es un servidor de páginas web. Sirve páginas html y admite plugins, por ejemplo, para php.

Tomcat puede funcionar como servidor de páginas web (html) y permite usar servlets y páginas jsp. Puede funcionar independiente o como plug-in de apache. Se aconseja el primer uso para tareas de desarrollo, pero al no ser demasiado eficiente con páginas html, se aconseja usarlo como plug-in de apache si el servidor se pone de cara al público. Tomcat no traga EJBs de J2EE.

JBoss es un servidor de aplicaciones j2ee. Este puede funcionar independientemente o bien asociado a Tomcat. No sé si de forma independiente traga con páginas html, servlets y páginas jsp, aunque supongo que sí. Viene además con su propia base de datos Hypersonic, aunque se puede cambiar por una externa.

Resumiendo, para pruebas, según lo que se quiera, se puede instalar sólo JBoss o sólo Tomcat o ambos juntos. Para para el servidor de verdad que va a dar servicio al público, va a haber que instalar las tres cosas.

En cuanto al rollo de los ejbs cada vez ando más desesperado. Soy incapaz de encontrar un hola mundo tonto y que alguien te diga la forma de ponerlo en un servidor de aplicaciones de estos sin necesidad de llamar a unos "deployer" misteriosos, que lo hacen todo solitos y tú no te enteras de nada. Me gustaría encontrar en un tutorial el cómo poner a mano los jar con los ejb, los ficheros war, ear y demás necesarios con el contenido mínimo, etc.

El deployer de tomcat para servlets está poco documentado. Hay que poner en un fichero de propiedades estilo java una serie de propiedades (que en ningún sitio te dice con detalle cuales) y llamar a una tarea de ant para que lo haga ella solita. El servlet me preocupa menos, porque ese sí he sido capaz de ponerlo a mano.

El servidor de aplicaciones de sun que viene con j2ee lleva una ventanita en la que ponemos un montón de parámetros y él solito lo hace todo.

El de jboss viene bastante mejor documentado que el tomcat, pero es igual de misterioso. En la documentación de remiten al tutorial de sun para que te bajes los fuentes de los ejb de prueba. En vez de ser unos ejb de "hola mundo" son una pequeña aplicación de un banco. Luego, te bajas un zip de jboss con unso ficheros extraños, lo juntas todos, ejecutas unos comandos de estilo ant y en mi caso obtengo un montón de errores. Lo de los errores debe ser habitual, porque en el mismo tutorial de jboss viene algo así como "si en este paso obtienes errores, revisa los ficheros xml, war, web son correctos, etc, etc)" Pues efectivamente, obtengo errores y no sé si los ficheros xml son correctos porque o bien me los he bajado o bien los ha generado el deployer ese y además estoy intentando aprender, si no, no estaría en ese tutorial.

Mi conclusión final es que los ejb son algo complicadísmo de poner en el servidor de aplicaciones, que todo el mundo lo hace con herramientas misteriosas y que nadie sabe qué es exactamente lo que está haciendo.

Bueno. Yo seguiré intentándo poner un hola mundo a mano.

04 diciembre 2005

j2ee

Después de mucho tiempo de andar pensando en ello, por fin me he decidido a hacer unas pruebas con j2ee.

Me he bajado el j2ee y el tomcat, como servidor de aplicaciones. El j2ee viene con su propio servidor de aplicaciones, de hecho, no he encontrado el j2ee solo. De todas fomas, parece que el servidor de apliaciones más conocido, dentro de los gratuitos, es el tomcat, así que sigo con él.

El tomcat puede instalarse en tres modos. Como plug-in de apache, o bien para que funcione sólo en dos modos distintos. Lo normal, por lo visto, es instalarlo como plug-in de apache, ya que apache es un servidor de páginas web al que se pueden añadir plug-ins de php, de j2ee (el tomcat), etc y es más eficiente. El tomcat aislado sólo sirve para j2ee/servlets. No se le puede poner php y además parece ser que es menos eficiente. Como de momento sólo tengo interés en jugar con el j2ee y no me apetece instalarme el apache, instalo el tomcat como autónomo.

Con el tomcat instalado y sin necesidad de j2ee puse en marcha mi primer servlet de "Hola mundo". No ha habido demasiado problema. Ha bastado con crear la clase servlet, ponerla en su sitio y con el administrador de aplicaciones de tomcat, ponerla en marcha. Para acceder a ese administrador de aplicaciones, basta acceder a http://localhost:8080 y sale una página de bienvenida a tomcat. En el lado izquierdo hay un menú en el que aparece el administrador de aplicaciones.

Lo del EJB ya me ha costado un poco más (de momento no lo tengo funcionando). Resulta que te haces tus clases EJB y sus interfaces (algo sencillo, en plan "hola mundo", copiado de openEJB) y cuando ya lo tienes todo, hay que "desplegarlo" (deploy) y eso es algo que no es trivial. Tomcat viene con una aplicación que se descarga por separado y es con el ant.

Buscando en la página de tomcat las instrucciones de esa aplicación con ant, he visto que está un poco incompleta. Para que funcione hace falta poner un montón de variables en un fichero de properties estilo java, pero cuando van a enumerar esas variables y contar como se usan, parece que se han saltado el parrafo que lo hace. Mi primera prueba lo único que consiguió fue borrar todos los fuentes de EJB que había creado.

En fin, volveré a ponerlos y a inentarlo ...

Mis conclusiones sobre maven

Aunque en algunas cosas la herramienta maven no me acaba de convencer, por ejemplo:
  • si utilizas proxy hay que poner el usuario y password en texto claro en un fichero xml de configuración, que aunque es local y debe estar lo suficientemente protegido, no deja de ser una cosa fea.
  • Hay que poner los nombres de jar y versión de las que dependes, con lo que si tu proyecto tiene varios jar y los vas actualizando, debes andar cambiando números de versión por varios xml. Esto está bien para generar versiones concretas muy de vez en cuando (que supongo que es para lo que está pensado), pero es un poco rollo para desarrollo, en el que se van generando versiones de los jar cada poco tiempo.
Sin embargo, hay varias cosas que me gustan y por lo que me decido a usarlo un poco más en serio a ver qué pasa.

  • La primera idea que me ha gustado es que para cada jar de mi proyecto debo hacer un proyecto de maven. En ese proyecto debo poner claramente las dependencias con otros proyectos. Esto obliga a ser organizado y si un fichero1.jar depende de un fichero2.jar y al revés, al menos está claro que dependen el uno del otro (cosa que no debería ser) y al tener que ponerlo explicitamente, no se nos puede colar esta dependencia recíproca de manera inadvertida.
  • La otra cosa que me gusta es que los jar quedan en un sitio único. Actualmente tengo un jar de componentes comunes para todos los proyectos, que al final siempre me tengo que andar llevando y copiando de un proyecto a otro y siempre tengo lio con la versión que estoy usando de ese jar de componentes. Con maven este jar queda en un sitio único claramente identificado con su versión. En los proyectos de maven sólo tengo que poner que lo necesito y qué versión, ya se encarga maven de todo.
  • Finalmente, otra cosa buena es la parte de "assembly" en la que maven me construye un zip con todos los jar y ficheros necesarios para ejecutar el proyecto. De esta forma llevarse el proyecto al sitio donde se va a ejecutar finalmente es casi inmediato y se lleva también muy bien el control de versiones del "ejecutable" que se tienes. Incluso si en ese zip se meten los fuentes, es posible tener todo para corregir un fallo concreto, depurar, etc, etc.
Y finalmente, lo mejor de todo, es que en mis proyectos e ido organizando mis directorios como mejor me ha parecido. Luego, durante el proyecto me he enfrentado a los problemas de la estructura de directorios que había inventado. Luego en el siguiente proyecto modificaba la estructura para tratar de arreglar esos problemas, pero me encontraba con otros que no había pensado y así sucesivamente. Maven parece que ofrece una forma de hacer las cosas y estructura de directorios que es la adecuada. Ya veremos cuando lo use algún tiempo en serio...

03 diciembre 2005

Tu propio mapa de visitantes

En http://www.mapservices.org/myguestmap/showindex.do puedes darte de alta y te dan una especie de mapa del mundo para tí.

Puedes meter ese mapa en tu página web y la gente que la visita puede meter marcas en el mapa, para indicar, por ejemplo, su nombre y dónde está situado en el mapa.

Es gratuito y lo he puesto en mi página de C++ y de Java para probar. Ahi tienes el link del mapa: http://www.mapservices.org/myguestmap/showguestmap.do?id=chuidiang

02 diciembre 2005

maven

Maven es una herramienta que se ejecuta a base de comandos en una shell/ventana de ms-dos y que permite unificar los proyectos y compartir jar entre ellos. Es configurable y todas las cosas por defecto se pueden cambiar.

maven organiza un almacen de jars local para el desarrollador. Ahi deja todos los jars que se vayan necesitando (tanto standard como log4j.jar, junit.jar, como los creados por el propio desarrollador u otros desarrolladores del equipo). También busca jar en almacenes en red, bien de internet, bien de algún servidor propio que nos instalemos, compartido entre todos los desarrolladores. Por supuesto, estos sitios de búsqueda se deben configurar.

Un proyecto con maven puede seguir más o menos estos pasos:

- Creacion del proyecto. con algo parecido a "
mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=MiProyecto" se crea sola toda la estructura de directorios necesaria (fuentes, class, test, ficheros de configuracion, etc). Genera un fichero pom.xml con los datos del proyecto por defecto.

- Tocamos el pom.xml para añadir dependencias (log4j.jar, junit.jar, etc). También podemos meter jar nuestros propios que hayamos almacenado previamente con maven en alguno de los alamcenes de los que hablabamos al principio.

- Creamos nuestro código fuente, ficheros de configuracion y clases de Test en los directorios adecuados.

- El comando "mvn package" busca las dependencias en el almacén local y si no están, las busca en los almacenes en red, compila todo, ejecuta los test y genera el jar.

- El comando "mvn deploy" se lleva nuestro jar de nuestro proyecto al almacen público de jars, de forma que ya se puede utilizar en otros proyectos y por otros desarrolladores.

- El comando "mvn assembly" genera un .zip con todo lo que se necesita para la ejecución: nuestro jar, los jar de los que dependemos y todos los ficheros de configuracion, iconos, etc. Esto es lo que se debe uno llevar al entorno de pruebas/entregar al cliente para que ejecute.

Esta es la idea básica.

Los ejecutables de java, los .jar y los .bat

Desde java no puedes crear ejecutables ( un .exe ) y tampoco es deseable. Si creas un .exe pierdes la portabilidad. Un .jar o los .class los puedes ejecutar en linux, en windows, en macintosh y donde quieras. Un .exe sólo corre en windows.

Con los .java, al compilarlos, obtienes los .class. Con esos .class ya se puede correr el programa con algo como esto

java ClaseConElMain

Como un programa suele llevar muchos .class y es complicado llevarlos todos de un sitio a otro. lo normal es meter todos los .class en un .jar. Un .jar es un fichero similar a los .zip de winzip (de hecho, puede abrirse con el winzip para ver su contenido). Es un fichero en el que están empaquetados y comprimidos otros ficheros. Se hace con el comando jar de esta manera

jar cf fichero.jar fichero1.class fichero2.class fichero3.class ...

Este .jar también se puede ejecutar. El comando es algo como esto

java -classpath fichero.jar ClaseConElMain

Dentro del jar puedes además meter un fichero de texto con un nombre especial, llamado fichero de manifiesto. En este fichero tú puedes indicar entre otras cosas, cual es la clase principal (la que tiene el main). Si este fichero de manifiesto está también dentro del jar, entonces la ejecución es un poco más sencilla. Se puede poner esto

java -jar fichero.jar

En este caso, si a windows lo configuras para que los ficheros .jar los abra con java -jar (igual que los .pdf se abren con acrobat, los .doc con word, etc, etc), entonces te bastará para ejecutarlo con hacer doble click sobre él.

Es bastante habitual que tu programa java dependa de varios .jar. Por ejemplo, si tu programa java trabaja con una base de datos oracle, necesitarás un jar que se llama ojdbc14.jar que te proporciona oracle. Para correr tu programa e indicarle que depende de ese .jar, debes hacer algo como esto

java -classpath ojdb14.jar -jar fichero.jar

Nuevamente, esta dependencia podrías meterla en el fichero .jar, dentro del fichero de manifiesto. En este fichero se puede poner una linea diciendo que se necesita el ojdbc14.jar y si ambos .jar están juntos en el mismo directorio, volvería a bastar para ejecutarlo con

java -jar fichero.jar

Hay otros temas. Por ejemplo, puedes querer que tu programa java lea determinadas variables de entorno. Estas variables deben pasarse al programa asi

java -Dvariable=valor -jar fichero.jar

Todo esto suele complicar la ejecución del programa. Por lo que es habitual hacer un fichero .bat (o .sh en linux) en el que simplemente se ejecute este comando o se definan determindas variables de entorno para ejecutar el programa. El .bat no es más que un ficherito de texto en el que está el comando anterior.

Además, en windows el .bat siempre se ejecuta al hacer doble click sobre él, mientras que el .jar, como es un fichero de empaquetado de otros ficheros, suele por defecto abrise con el winzip y necesita ser configurado. Si tu das tu aplicación en un CD a otra persona para que la ejecute, sobre todo si no tiene ni idea de java, le resultará más cómo hacer doble click sobre un .bat que andar configurando windows para que el .jar se ejecute con java o ejecutandolo desde linea de comandos con algo tan feo como

java -jar fichero.jar

En resumen, el .bat no es necesrio en absoluto, pero suele hacerse por comodidad de ejecución.

Finalmente, si tu programa java es lento y necesita ejecutarse de forma más eficiente, hay herramientas por internet (java2exe, gcj, jbuilder, etc) capaces de convertir el .jar en un .exe de windows. Con esto teóricamente ganas velocidad en la ejcución, pero pierdes totalmente la portabilidad del programa (sólo corre en windows).

30 noviembre 2005

FOLDOC - Diccionario de computación

En FOLDOC hay un diccionario, en perfecto inglés, sobre terminos de programación.

Esta bastante bien para buscar todas esas siglas que aparecen en todos sitios y no sabemos qué significan o qué es exactamente lo que son.

29 noviembre 2005

Cetus Links

En Cetus Links hay una hartá de enlaces sobre temas de programación, en perfecto inglés.

Diagramas UML para java

Una herramienta de dibujo UML para java gratis: fujaba.

No la he probado y no tengo ni idea de qué tal va, pero parece interesante de mirar...

26 noviembre 2005

Las buenas prácticas de programación

Todos sabemos que está prohibido hacer variables globales, que no se pueden poner atributos publicos, no se debe usar goto, etc, etc.

¿Por qué es un pecado?

Todas las buenas prácticas están pensadas para evitar problemas después, no para evitarlos ahora. Me explico. Si yo quiero hacer un programa rápido, lo más rápido posible y no pienso tocar ese código nunca más, ni pretendo reaprovecharlo, ni nadie tiene que mantenerlo ni añadirle mejoras, las buenas prácticas de programación no sólo no sirven para nada, sino que además son un estorbo que me harán programar más despacio. En ese caso sí se pueden usar goto, atributos públicos, no comentar código, etc, etc.

Sin embargo, esto no es lo habitual. Un programa, sobre todo si es para el trabajo, siempre corre prisa, pero siempre hay que introducir luego mejoras, nunca funciona a la primera y siempre hay que depurarlo. Siempre nos sale otro programa después que se parece al primero y hace que intentemos reaprovechar el primero. Y aunque no pensemos usar el programa nunca más, nunca podemos estar seguros de ello.

Por ello
  • Comentar el código es totalmente inútil si nunca nadie va a mirar ese código. Es casi obligatorio si tenemos que mirarlo dentro de unos meses o tiene que mirarlo otra persona que quiere reaprovecharlo, corregirle fallos a añadirle funcionalidades.
  • Las variables globales impiden reaprovechar el código. No podremos llevarnos un trozo de código sin esa variable global y quizás sin el resto de código que modifica esa variable. Tampoco sabremos en el efecto en el resto del código si aquí hacemos alguna moficiación que modifique el valor de la variable global. Buscar por todo el código dónde se usa la variable global puede ser un infierno, sobre todo si la variable global se llama "a".
  • Los atributos públicos son parecidos a las variable globales. No sabemos que efectos puede tener en el resto del código el cambio de esa variable.
  • Los goto sí que hacen impredecibles los cambios en el código. Nunca sabemos quién puede venir al trozo de código que estamos modificando ni desde dónde. Si hacemos funciones y llamamos a las funciones, podemos modificar el código dentro de la función siempre que aceptemos los mismos parámetros y devolvamos lo mismo.
y así hasta el infinito.

23 noviembre 2005

JFrame y JDialog

¿Qué diferencia hay entre un JFrame y un JDialog? ¿Cuando usar uno u otro?.

Teóricamente, en una aplicación debe haber un único JFrame, que es la ventana principal de la aplicación.

En windows, el JFrame aparece además de como ventana, en la barra de tareas. El JDialog no aparece. Si queremos que nuestra aplicación apareezca en dicha barra y sólo aparezca una vez, debemos usar un único JFrame, correspondiente a la ventana principal de nuestro programa.

Al JFrame además podemos llamar al método setIconImage(), para hacer que el icono de la taza de café propio de java cambie por el icono que nosotros queramos.

El resto de las ventanas de nuestra aplicación deberían ser JDialog. Estos JDialog no aparecen en la barra de tareas y además, en el constructor, se les puede pasar un padre. Al pasarles un padre, siempre se visualizarán delante del padre. Si les pasamos como padre el JFrame de nuestra aplicación, siempre saldrán encima de él y no se iran detrás. Una ventaja adicional de pasarles como padre el JFrame es que heredan de él el icono que pusimos con setIconImage(). El JDialog no tiene este método y la única forma de cambiar la taza de café es pasarle como padre otra ventana que tenga el icono cambiado.

21 noviembre 2005

Artículos de técnicas de diseño en java

En http://www.artima.com/designtechniques/index.html he encontrado una lista de artículos sobre diseño en java. Tiene bastante buena pinta. Miraré alguno que me interese y ya comentaré que tal.

20 noviembre 2005

Para crear un foro gratis

Me he encontrado una página http://www.foroswebgratis.com/ en la que te registras y te dan un foro. Puedes meterlo en tu página web o simplemente ellos te lo crean y tú tienes el link. Por supuesto, tú eres el administrador del foro.

De momento, para probarlo, lo he añadido a mi página de java. Y este es el foro.

Tutorial de Swing y JFC (Java Foundation Classes)

En http://www.programacion.com/java/tutorial/swing/ hay un tutorial con buena pinta sobre java swing.

19 noviembre 2005

www.links-rotos.com: Comprueba tus enlaces

En http://www.links-rotos.com/ hay una página a la que le das la url de la tuya y se dedica a revisarla, buscando links rotos. Al final te da un informe de los links que hay en tu página y que no funcionan.

Está limitado a un número concreto de páginas dentro de tu host y un número de páginas en el exterior, además de un tiempo de exploración de 15 minutos.

Está bastante bien para poder revisar los enlaces de tu página web.

18 noviembre 2005

La leche de geocities

Tengo una paginilla en http://www.geocities.com/chuidiang

El problema es que va aumentando de visitas y los de geocities me limitan el tráfico. Cada vez con más frecuencia, al entrar en la página, me sale un aviso indicando algo así como "esta página ha excedido el límite de tráfico permitido y no está disponible durante una hora".

Como no me apetece pagar para aumentar el limite ese, pues creo que voy a tener que ir llevándome páginas a otro sitio, al menos para repartir.

14 noviembre 2005

Documentación de java

En http://java.sun.com/j2se/1.5.0/docs/index.html hay un bonito gráfico con todos los paquetes de la edición standard de java 1.5

Lo interesante del asunto, es que pinchando en el bloque que queramos, accedemos a la documentación de ese paquete. No a la api de java, sino a una documentación en la que se explican cosas interesantes, como qué se puede poner en un fichero de manifiesto de un jar, la filosofía de swing y awt, etc, etc.

13 noviembre 2005

Servidores web gratis con mysql y php

En http://www.free-webhosts.com/power-search.php hay un buscador de servidores web gratuitos.

Únicamente tenemos que marcar qué características queremos que tenga el servidor (php, mysql, etc) y darle a buscar.

12 noviembre 2005

SpywareInfo > Online Spyware Detection

Con todo el tema del emule este y el kazaa sobre todo se me ha llenado el ordenador de "parásitos" indeseados, adware variados y popups a mogollón.

Todos esos parásitos, básicamente son cokies y programitas que se instalan con la intención de transmitir a alguien las páginas por las que navegas, información de tu ordenador, mostrar popups de propaganda, etc, etc.

SpyBot, el programa habitual que uso para eliminar estos bichos, eliminó muchos de ellos, pero no fue capaz de eliminar dos en concreto. Los detectaba, me decía que no podía eliminarlos salvo que reiniciara el ordenador. Reiniciaba el ordenador y el SpyBot me mandaba reiniciar otra vez.

Me bajé AdWare (Otro programa gratuito para quitar adware) y lo pasé. Me encontró más cosas que el SpyBoot y quitó todas sin problemas.

Para más seguridad todavía, me fuí a la página http://www.spywareinfo.com/xscan.php, que si se entra con internet explorer y se le da permiso, te hace un scaneo completo. Este encontró unas cuantas cosas más que borró. Espero que ya esté todo limpio.

11 noviembre 2005

Tutorial de Java - Tabla de Contenido

En http://www.itapizaco.edu.mx/paginas/JavaTut/froufe/parte11/cap11-11.html parece que hay un tutorial de java, que debe ser de Froufre. En él hay algunos temas interesantes que no se tratan en otros tutoriales, como el modelo de delegación de eventos.

09 noviembre 2005

Foro phpBB

Hace tiempo intenté instalar en el servidor apache/php que tengo en el trabajo el foro phpBB. Este foro no es más que un montón de páginas php que se copian en el directorio web en el que se quiera que esté el foro y ya casi está. Sólo hay que configurarlo.

El administrador del foro debe entrar con su navegador en la página del foro y meter unos datos de configuración más o menos simples (tipo de base de datos usada, usuario y password para la base de datos, correo del supervisor del foro, etc, etc). Una vez que le da a "Aceptar", se crean automáticamente en base de datos todas las tablas necesarias y todo está funcionando.

Esto en teoría es fácil. De hecho, la página phpBB2 en su ayuda para la configuración da cuatro ideas tontas y dice algo así como "si no eres capaz de configurarlo, no eres digno/capaz de mantener un foro".

Pues yo no soy digno. Con la versión phpBB 2.0.13 fuí incapaz de meter los cuatro datos tontos que pide. Daba error al pulsar "Aceptar", pero no te decía el error ni ninguna pista de lo que estaba mal. Por más que lo intenté y cambié parámetros, no fui capaz de hacerlo.

Sin embargo, mucho después decidí volver a intentarlo. Me bajé la versión phpBB 2.0.18 y ¡misterios de la ciencia!, metí los cuatro datos tontos y se instaló sin ningún problema. Ya tengo el foro funcionando en el trabajo. Ahora sólo queda saber para qué sirve. Parece que la gente no le ha hecho mucho caso, así que de momento el único que escribe soy yo.

08 noviembre 2005

Undo - Redo en java

Java tiene un paquete javax.swing.undo que da soporte para hacer undo-redo en algunos componentes java.

Los Document (que van metidos en JTextField y similares) también tienen soporte, por lo que hacer undo - redo de los JTextComponent es casi inmediato.

El funcionamiento es básicamente el siguiente:

  • Los Document admiten un suscriptor a cambios undoables. Para suscribirse hay que llamar al método addUndoableEditListener().
  • javax.swing.undo tiene una clase UndoManager que implementa la interface UndoableEditListener, que se puede suscribir a esos cambios en los Document.
  • La clase UndoManager va guardando todos los cambios de todos los sitios en los que está suscrita. Tiene métodos de undo() y redo() para hacer y desacer los cambios de los que se ha enterado de forma secuencial, además de otro montón de métodos útiles.
En resumen, para hacer undo - redo en los JTextField, hay que hacer un código similar a este

UndoManager um = new UndoManager();
textField1.getDocument().addUndoableEditListener (um);
textField2.getDocument().addUndoableEditListener (um);
...
// cuando venga bien (pulsar algún botón que ponga "undo") se puede hacer
um.undo();


Una pequeña tontería. Todo esto lo he mirado, pero no lo he probado. Es posible que no funcione o haya alguna pega que no está aquí.

07 noviembre 2005

Google PageRank

En http://pr.efactory.de/ he encontrado una página que explica con bastante detalle cómo funciona el page rank de google.

El page rank es una puntuación que da google a cada página web. Cuanto más alta sea esa puntuación, antes saldrá la página en el buscador. De esta forma las páginas con mayor page rank son las que salen primero en el buscador.

Esta puntuación se obtiene a base de los enlaces que hay entre páginas. Cuando una página A tiene un link a una página B, ese link equivale a un "voto" de la página A por la página B.

Todos los votos no valen lo mismo. El voto de una página A por una página B es más valioso teniendo en cuenta los siguientes factores:

- Cuanto mayor sea el page rank de A, más vale el voto.
- Cuantos menos links hacia otras páginas tenga la página A, más vale el voto.

El page rank de una página es la suma del valor de todos los votos a esa página.

Es curioso, pero para tener un page rank alto, basta con que una única página importante con pocos links ponga un link a ella.


El navegante errático.

Una cosa que me ha llamado la atención es que la fórmula de cálculo del page rank está basada en el cálculo de probabilidad de que un navegante, pinchando enlaces al azar, acabe en esa página.

Cuanto mayor sea el page rank (entendido como probabilidad) de una página que enlaza con la tuya, más probabilidades hay de que el navegante se encuentre en esa página, por lo que aumentan las probabilidades de que pinche en la tuya.

Sin embargo, cuanto más enlaces haya en esa página, menos probabilidades hay de que pinche en el enlace a la tuya. Por eso el tema de dividir por en número de enlaces.

05 noviembre 2005

Otra más sobre e-mule

Seguí haciendo pruebas con el tema de que e-mule me dejaba colgado el ordenador. Probé e-mule Plus, eDonkey 2000 (que es compatible con e-mule) y algún otro cliente (no recuerdo si era iMesh o lphant). El caso es que todos ellos se me dejaban colgado el ordenador en algún momento.

Con kazaa no, todo funciona bien (y dicho sea de paso, me descarga las cosas más rápido, aunque hay menos cosas que descargar).

Otra prueba que hice es cambiar la tarjeta de red. Tengo una D-Link DWL 520+ y la cambié por una Belkin Wireless 54Mbps y con ella todo funciona correctamente. Al final la conclusión es que e-mule no es muy compatible con esta tarjeta.

Ya de paso, comparando ambas tarjetas, recibo mejor la señal con la D-Link. Supongo que el hardware de captura de señal es mejor es esta tarjeta (también hay una diferencia de precio importante).

04 noviembre 2005

Sincronización de hilos en java

No soy ningún experto en hilos, pero voy a contar aquí un poco de lo que sé. Quizás no sea lo más correcto, pero es la forma en la que yo los uso y me funcionan.

Para sincronizar hilos tenemos dos formas.

Sincronizar los métodos:

Por un lado podemos poner synchronize en los métodos de una clase. Sería algo como esto:

class Clase
{
public synchronized void metodo()
{
....
}
}

Si varios hilos llaman al método a la vez, sólo el primero consigue entrar. Los siguientes se quedan a la espera de que el primero termine.

Supongamos ahora que hay varios métodos synchronzied. Si un hilo entra en uno de ellos, todos los demás métodos synchronized quedan bloqueados para los demás hilos. No pueden llamar a ninguno de los demás métodos hasta que el primer hilo termine con el suyo.


Sincronizarse con un objeto:

Por otro lado podemos hacer un código sincronizado con un objeto. El código sería así

synchronized (cualquierObjeto)
{
// código que usa cualquierObjeto
}

Esto requiere la colaboración de todos los hilos. Cualquier hilo que quiera acceder a ese cualquierObjeto, debería hacerlo así. Mientras un hilo está en su trozo de código synchronized, ningún otro hilo puede acceder al cualquierObjeto.

¿Cuándo se usa uno y otro?

El primer sistema está bien para proteger atributos de la clase, de forma que ningún hilo los cambie mientras se está ejecutando uno de sus métodos. Sin embargo presenta una pequeña pega. Supongamos que tenemos una clase Lista con dos métodos sincronizados, un dameNumeroElementos() y un dameElemento(int i). Si un hilo quiere obtener los elementos, hará algo como esto

for (int i=0; i<lista.damenumeroelementos();>
{
dato = lista.dameElemento(i);
// tratamos el dato
}

Mientras se está haciendo el bucle, cualquier otro hilo puede incordiar. Mientras pedimos el número de elementos o pedimos un elemento, ningún otro hilo tiene acceso, pero mientras se está incrementando la i, se trata el dato o se compara si hemos llegado al final del bucle, cualquier hilo puede modificar la lista. En concreto puede pasar que el dameNumeroElementos() nos diga que hay un elemento, que otro hilo lo borre antes de que accedamos a él y que luego intentemos pedirlo, con el consiguiente error.

Cuando necesitamos hacer algo con un objeto llamando a varios métodos (como en el caso de obtener los elementos de la lista), es mejor usar esto

synchronized (lista)
{
for (i=0;i<lista.damenumeroelementos();i++
{
dato = lista.dameElemento(i);
// tratatmos el dato
}
}

Por supuesto, los el código de los demás hilos debe respetar este mecanismo, poniendo código sincronizado con lista cuando se quiera hacer algo con ella.

Este mecanismo también es bueno cuando el objeto en cuestión representa un recurso externo (por ejemplo, un InputStream o OutputStream de un fichero o un socket, o una Connection de una base de datos). Si nos sincronizamos con ese objeto, evitamos que varios hilos escriban o lean a la vez de ahí, impidiendo que los mensajes salgan o entren entremezclados.

03 noviembre 2005

La nueva metodología (Martin Fowler)

La página de Martin Fowler parece bastante interesante. Rebuscando en ella he encontrado un artículo (en cristiano) sobre las metodologías de diseño, haciendo especial mención de las metodologías ágiles.

Todavía no lo he leido, pero voy a ello...

Bueno, ya casi me lo he leido. No sé si es por la traducción o qué, pero se me hace un poco pesado de leer. Para cuatro ideas sencillas que cuenta, usa frases demasiado enrevesadas y que hay que leer despacio para comprender.

La idea es la de siempre, no hay que perderse haciendo documentación. El programador cobra impotancia dejándole decidir cómo hacer las cosas y en qué tiempo. Hay que hacer iteraciones en espacios cortos de tiempo (de un par de semanas a un mes) e ir dándoselas al cliente para que las prueba y decida sobre la marcha que quiere después. La programación extrema es una de las metodologías ágiles.

02 noviembre 2005

Poner imagen de fondo en una ventana

He estado haciendo unos experimentos para ver si consigo poner una imagen de fondo en una ventana. No sé si he llegado a la mejor solución, pero aquí hay una.

Lo primero es hacer una clase que herede de Container y redefina el método paint(Graphics g) de esta manera

class Contenedor extends Container
{
// La imagen que queremos de fondo, un fichero .gif
public ImageIcon icono = new ImageIcon ("./imagen.gif");

// Redefinición del método paint()
public void paint (Graphics g)
{
// Borramos todo y lo pintamos del color de fondo por defecto.
Rectangle r = g.getClipBounds();
g.setColor(this.getBackground());
g.fillRect (r.x, r.y, r.width, r.height);

// Pintamos la imagen
g.drawImage (icono.getImage(), 0,0,this);

// Hacemos que se pinten los botones dentro de este contenedor
super.paint(g);
}
}

Luego basta con instanciar un JFrame o JDialog y meterle este contenedor

JFrame ventana = new JFrame();
ventana.setContentPane(new Contenedor());

Con esto vale. Como pega es que la imagen no se repite si la ventana es más grande que la imagen. Esta sólo sale una vez.

Los componentes que se pinten en la ventana, (JButton y demás, salvo el JLabel), tienen su propio color de fondo y machacan la imagen. Si no se quiere así, habría que ir llamando a los método setOpaque(true) de los compoentes.

Unas variantes para esto pueden ser las siguientes:
  • En drawImage() podemos hacer que la imagen coja el tamaño del contenedor. Para ello hay que poner unos parámetros más e incluso el color de fondo: g.drawImage (icono.getImage(), 0, 0, this.getWidth(), this.getHeight(), this.getBackground(), this);
  • Si no damos tamaño a la imagen, pero sí ponemos el color de fondo, nos ahorramos borrar antes el contenedor. La pega es que si el contenedor es más grande que la imagen, todo ese sobrante no se borra.

Más sobre emule

Al final nada de nada. Por más pruebas que hago, el emule me deja colgado el ordenador.

Con las versiones antiguas (0.44d) no consigo nada, se sigue quedando colgado. También he probado con el emule plus sin resultado.

Mirando foros, he visto que determinados router y tarjetas de red dan problemas, generalmente de desconexión con internet, al usar emule. Hay gente que ha conseguido arreglar el problema actualizando drivers o con scripts que configuran las tarjetas de red de alguna forma especial. En mi caso, no he tenido suerte (aunque tampoco me lo he tomado muy en serio).

Mi conclusión final es que algunas tarjetas de red / routers no funcionan bien con emule. Como de momento no tengo intención de cambiar de tarjeta de red, paso de emule.

Al final he probado con kazaa. Me gusta menos la red de kazaa (hay menos cosas), pero al menos me funciona. kazaa mete mucha propaganda, pero he leido por ahi que hay una versión (kazaa lite) que es lo mismo, pero sin la propaganda ni spywares ni demás. Ya me la he bajado y algún día la probaré.

29 octubre 2005

Más de emule

Pues nada, lo del emule ni patrás.

Con la versión 0.44d se cuelga incluso antes que con la 0.46c
Al menos con esta última, si estoy trabajando o pongo música a la vez tarda más en colgarse (acaba haciéndolo).

En los foros también se mencionan problemas con la conexión, los moden, routers y demás. Yo sé que en ocasiones mi tarjeta de red (d-link airplus dwl 520+), mientras busca la red, deja el ordenador completamente colgado durante unos segundos, exactamente igual que hace el emule, pero con la diferencia de que el emule lo hace de forma permanente.

Me he bajado un driver nuevo para mi tarjeta, pero sigo con el mismo problema. Seguiré investigando y si es posible, trataré de pedir otra tarjeta prestada para ver si con el cambio de tarjeta no hay problema.

27 octubre 2005

Una de eMule

El otro día decidí probar todo el tema de eMule para ver qué tal va y que no me suene a chino.

Me bajé la versión 0.46c que parece que es la última.

El tema está bien, te puedes bajar montones de cosas. Lo que no me gusta demasiado es que no tienes control ninguno (o casi) de las descargas. Las cosas van bajando solas, cuando les apetece (o pueden), a la velocidad que pueden. Es normal que intentes bajar ficheros y no comiencen a descargar. En fin, es algo para tener encendido y corriendo en background mientras vamos trabajando.

Se me ha presentado un problema más grave. Cuando estoy trabajando con el ordenador, no pasa nada, pero si lo dejo sólo con el eMule puesto, el ordenador se cuelga. No funciona el ratón ni el teclado ni nada de nada. Hay que apagar el ordenador a lo bestia.

Mirando por los foros de internet, he visto que el problema es relativamente habitual (junto con desconexiones completas de internet). También en esos foros he visto que el problema se presenta a partir de la versión 0.45a y posteriores.

Aunque las anteriores presentan algunos problemas de seguridad (según los foros), supongo que es mejor pasarse a una más antigua y no tener que resetear el ordenador cada cuarto de hora.

26 octubre 2005

Captura de teclas en un JTextField de java

Para limitar el tipo de caracteres o la entrada en un JTexField, tenemos muchas posibles soluciones.

En http://members.lycos.co.uk/chuidiang/ejemplos/JTextField/limita_caracteres.html hay una en la que se usa un Document.

Otra solución de más bajo nivel consiste en capturar las teclas según se pulsan y decidir si se aceptan o no para escribir en el JTextField. Por ejemplo, podemos capturar la tecla y escribirla en el JTextField sólo si es un número y rechazarla si es una letra.

Para ello, tenemos que añadir un KeyListener a nuestro JTextField. Cada vez que se pulse una tecla, se avisará al método keyTyped(KeyEvent e) de nuestro KeyListener. Del KeyEvent que nos pasan como parámetro podemos obtener qué tecla se ha pulsado (qué caracter) y rechazarla o no según nos convenga.

Por ejemplo, para aceptar sólo números, el código sería este:

JTextField textField = new JTextField(10);
textField.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{
char caracter = e.getKeyChar();
if(((caracter < '0') ||
(caracter > '9')) &&
(caracter != KeyEvent.VK_BACK_SPACE))
{
e.consume();
}
}
});

Además de comprobar si caracter no está entre 0 y 9, comprobamos también que se admita la tecla VK_BACK_SPACE, porque si no, no podremos borrar lo que escribamos.

El método e.consume() hace que esa pulsación de tecla se rechace y el JTextField no la trate. El resultado es que cualquier cosa que no sea una cifra o el backspace se rechaza. Si queremos meter decimales, deberíamos aceptar también el punto decimal.

Este método tiene sin embargo una pega respecto al Document. Si en otro lugar copiamos un cacho de texto y lo pegamos sobre nuestro JTextField, eso no es equivalente a pulsar las teclas del texto copiado de una en una, sino que simplemente habremos pulsado Ctrl-V (en windows). Este control el JTextField lo trata directamente y pega el texto dentro del JTextField, sin ningún tipo de comprobación de si es o no un número.

Por este motivo, salvo que queramos algún "efecto especial" como hacer "beep" cada vez que se pulse una tecla que no se debe, es mejor usar el Document, o bien, debemos hacer un tratamiento algo más complejo de las teclas a pulsar.

25 octubre 2005

Uso de strtok()

strtok() es una función de C que permite partir una cadena en subcadenas usando como separador los caracteres que queramos.

Supongamos que tenemos una cadena con una frase y queremos separarla en palabras. Como separador de palabras usaremos los espacios, comas, dos puntos, punto y comas y puntos.

Lo primero es hacer una copia de nuestra cadena original, puesto que la función strtok() la estropea (va reemplazando separadores por caracteres \0). Para la copia podemos usar strdup(), que creo que es propia de C en unix o bien strcpy() que sí es standard.

Para obtener el primer token (la primera palabra), hay que llamar a strtok() pasando como parámetros la cadena que queremos analizar (la copia) y una cadena con los caracteres separadores

char *primeraPalabra = strtok(cadena, " .,;:");

Si primeraPalabra es NULL, no hay palabras en la cadena. A partir de aqui, para que nos devuelva los siguientes tokens (palabras) hay que llamar a la misma función, pero pasando NULL como primer parámetro. La función recuerda la cadena y la última palabra que devolvió y continua con la siguiente.

Lo mejor es meter todo en un bucle

char *token = primeraPalabra;
while (token != NULL)
{
token = strtok (NULL; " .,;:");
// token es ahora la siguiente palabra o NULL. Aqui debemos guardarla en algún sitio,
// escribirla en pantalla o lo que queramos.
}

Precisamente por recordar strtok() su estado en sucesivas llamadas, no es una función segura para usar con hilos. Lo que hace un hilo con ella puede estropear lo que hace el otro. Tampoco podemos dentro de un mismo hilo ir analizando dos cadenas distintas en paralelo. Habría que analizar completamente una y luego la otra.

23 octubre 2005

El patrón composite

Aprovechando el tema de las interfaces y su ejemplo, el de sacar la salida por impresora, pantalla o base de datos, voy a contar un poco en qué consiste el patrón de diseño compuesto (o "composite").

Supongamos que el gracioso que mencionabamos en las interfaces, nos pide poder sacar en la pantalla y en la impresora a la vez. Una solución es hacerse una clase PantallaEImpresora que implemente Escritor y escriba en pantalla y en impresora.

Sin embargo, hay una solución mejor. Hacer la clase PantallaEImpresora tiene la pega tonta de que necesitamos repetir el código de nuestra clase Pantalla y de nuestra clase Impresora. En nuestro tonto ejemplo, el código es tonto y no se pierde mucho tiempo en repetirlo, pero en un programa serio, seguramente no se hace tan rápido. Además, ¿por qué reptetir el código que ya tenemos hecho, en contra de la filosofía de reutilización?.

Una solución mejor es hacerse una clase "compuesto". Esta clase guarda dentro una lista de Escritores y a su vez implementa la interface Escritor. En el método escribe(), lo que hace es ir llamando a los métodos escribe() de todos los Escritor que tiene guardados. Por supuesto, puede tener métodos para añadir o borrar Escritores sobre la marcha.

El código puede ser parecido a este

class CompuestoEscritores implements Escritor
{

// Lista de escritores
private LinkedList escritores = new LinkedList();

// Añade un escritor a la lista
public void addEscritor (Escritor escritor)
{
escritores.add(escritor);
}

// LLama al escribe() de todos los escritores
public void escribe (String cadena)
{
for (int i=0;i<escritores.size();i++)
{
((Escritor)escritores.get(i)).escribe(cadena);
}
}
}

Con esta clase tan tonta, podemos permitir que nuestro código anterior sea capaz de sacar los resultados en varios sitios a la vez, eligiendo qué sitios. Además, si inventamos un nuevo sitios (por ejemplo, enviar los resultados por red) bastará como antes hacer la clase Escritor correspondiente y podemos añadirla tanto aquí como a nuestro método de cálculos.

Una pequeña tontería, el código de CompuestoEscritores lo acabo de poner sobre la marcha, igual ni siquiera compila....

22 octubre 2005

Por qué usar interfaces

Una pequeña explicación de por qué usar interfaces al programar. Aunque los ejemplos son java, la idea vale para cualquier lenguaje orientado a objetos.

Imaginemos que hacemos un programa que echa unas cuentas y va presentando en pantalla unos resultados. Puede ser algo como esto

void metodoConLasCuentas()
{
double valor=this.cuentas(datos);
System.out.println ("El valor es "+valor);
double valor2 = this.otrasCuentas(datos);
System.out.println ("El otro valor es " + valor2);
}

Ya está el programa. Se lo enseñamos a nuestro jefe, a nuestro cliente, a nuestro mejor amigo o al gracioso de turno y nos dice ... "Me gustaría poder sacar eso por impresora".

Nos ponemos manos a la obra y cambiamos nuestro programa

void metodoConLasCuentas()
{
double valor=this.cuentas(datos);
this.sacaPorImpresora ("El valor es "+valor);
double valor2 = this.otrasCuentas(datos);
this.sacaPorImpresora ("El otro valor es " + valor2);
}

Volvemos a enseñárselo al graciosillo y nos dice ... "Es que yo querría poder elegir sobre la marcha si lo quiero en pantalla o en impresora".

Nuevamente nos ponemos a tocar el código, pero esta vez algo más complejo

void metodoConLasCuentas()
{
double valor=this.cuentas(datos);

if (porImprsora==true)
this.sacaPorImpresora ("El valor es "+valor);
else
System.out.println ("El valor es "+valor);

double valor2 = this.otrasCuentas(datos);

if (porImpresora==true)
this.sacaPorImpresora ("El otro valor es " + valor2);
else
System.out.println ("El otro valor es " + valor2);
}

Nuevamente el gracios de ideas infinitas nos dice ... "Maravilloso, pero sería bueno poder guardar esos valores en base de datos".

Hartos de tocar el código decidimos implementar una solución que nos valga para todas las peticiones estúpidas del graciosillo. La solución es usar una interface. Hacemos nuestra interface así

interface Escritor
{
public void escribe (String cadena);
}

Ahora hacemos varias clases que implementen la interface, una clase Pantalla con el System.out.println, una clase Impresora con el sacaPorImpresora y una clase ABaseDeDatos con algo que sea capaz de meter esa cadena en la base de datos.

Nuestro código queda así de bonito ahora

void metodoConLasCuentas (Escritor escritor)
{
double valor=this.cuentas(datos);
escritor.escribe ("El valor es "+valor);
double valor2 = this.otrasCuentas(datos);
escritor.escribe ("El otro valor es " + valor2);
}

Llamando a nuestro método pasándole una clase Pantalla, sacará el texto por pantalla, con una clase Impresora, lo sacará por impresora y con un ABaseDeDatos lo guardará en base de datos.

Ya no tenemos que tocar nunca más nuestro código. Únicamente hacernos una nueva clase para la nueva idea del graciosillo y llamar a nuestro método con la nueva clase.

Este es un ejemplo tonto, pero si nos fijamos en java, hay multitud de sitios donde se usa.
  • Un JButton admite una interface ActionListener con un método actionPerformed(). El botón está hecho una única vez para siempre, y pasándole una Interface ActionListener, ese botón hace lo que queramos nosotros. Esta vez la gente de java eran los programadores y nosotros los graciosillos que queremos que el botón haga no sé qué.
  • Un JTable admite una interface TableModel. El TableModel es la interface y somos nosotros los que debemos rellenar los métodos. De esta forma un JTable hecho una sola vez es capaz de pintar cualquier tabla de datos, sin necesidad de tocarla por dentro. Únicamente tenemos que hacernos nuestra clase TableModel y pasársela al JTable.
  • Un Thread admite una interface Runnable. Esta clase Thread es capaz de hacer que se ejecute en un hilo aparte cualquier cosa que nosotros queramos. Únicamente hay que implementar el método run() de la interface Runnable y pasarle eso a la clase Thread.
  • etc, etc, etc.

Un buscador de rpm

En http://rpmfind.net/ hay un buscador de rmp (paquetes para instalar en determinadas distribuciones de linux).

Simplemente escribimos el nombre del programa que queremos instalar y nos indica los rpm para descargar.

19 octubre 2005

La clase java.awt.Robot

La clase java.awt.Robot permite hacer todo lo que puede hacer un usuario. Esta clase simula pulsaciones de teclas, movimientos de ratón y clicks del mismo.

Hay dos posibles utilidades que se me ocurren, aunque seguro que hay más.

La primera es para hacer demos o ayudas. Podemos presentar una aplicación y hacer que el ratón se vaya moviendo de un sitio a otro, haciendo cliks y así ver cómo funciona la aplicación.

La otra posibilidad, quizás más interesante, es para las pruebas unitarias de una interface gráfica.

Cuando se hace código orientado a objetos con clases y demás, se pueden hacer lo que se llaman pruebas unitarias. Estas pruebas consisten en hacer un main() para cada clase y hacer código que pruebe esa clase, para saber si está bien o mal. Por ejemplo, si tengo mi clase Matematicas con un metodo suma (int a, int b) que me devuelve la suma de a y b, la prueba unitaria consistiría en hacer un main que instancie la clase Matematicas y que llame al metodo suma() con varios valores, comprobando que el resultado que devuelve es correcto.

Un requisito habitual de estas pruebas unitarias es que el que va a probar no tenga que hacer nada. Simplemente arrancar la prueba y esta debe darle un OK o un FALLO. Esto es fácil de hacer con determinadas clases, pero casi imposible cuando la clase es una ventana. Si al apretar el botón "ayuda" debe salir una ventana con la ayuda, es muy dificil sólo con código apretar el botón de "ayuda" y ver si sale la ventana.

Aquí es dónde entra la clase java.awt.Robot. Como puede simular el movimiento de ratón y el click, es posible con ella hacer que se pulse el botón "ayuda". En cuanto a ver si la ventana de ayuda es visible, bastaría con preguntarle ventana.isShowing(). Con la clase java.awt.Robot podemos llevar el ratón a un JTextField, hacer click en él para que gane el foco, simular la pulsación de teclas para escribir algo en él, mover el ratón a un botón "Aceptar" y pulsarlo, es decir, la secuencia típica de entrada de datos en un formulario.