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).