09 febrero 2006

Un botón girado

En java podemos heredar de JButton o cualquier otro componente y redefinir el método paint() para que el componente se dibuje como queramos.

Una opción fácil usando la clase AffineTransform consiste en hacer que el botón se gire, por ejemplo 45 grados. El siguiente código de ejemplo hace eso precisamente. Muestra una ventana con un botón girado 45 grados.

package chuidiang.ejemplos;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

public class EtiquetaGirada extends JButton
{
public void paint (Graphics g)
{
// Para girar
AffineTransform at = new AffineTransform();
at.scale(0.6,0.6); // Se reduce el tamaño para que quepa girado
at.translate (80,0); // Se traslada para que quede centrado
at.rotate(45*Math.PI/180); // Se rota 45 grados
((Graphics2D)g).setTransform(at);
super.paint(g);
}

public Dimension getPreferredSize()
{
return new Dimension (100,100);
}

public static void main(String [] args)
{
JFrame v = new JFrame();
v.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
EtiquetaGirada l= new EtiquetaGirada();
l.setText("hola");
l.setFont(l.getFont().deriveFont((float)30.0));
v.getContentPane().setLayout(new FlowLayout());
v.getContentPane().add(l);
v.pack();
v.setVisible(true);
}
}

Y este es el resultado



Bueno, no queda demasiado allá, pero es cuestión de arreglarlo un poquito.

06 febrero 2006

Juntar ramas en cvs

Lo prometido es deuda. Comenté que había hecho una rama en cvs y que iba a poner una nueva entrada en este diario cuando las juntara. Bien, ya lo he hecho.

El mecanismo es sencillo, aunque puede ser un poco tedioso si hemos dejado pasar mucho tiempo desde que hicimos la rama hasta que juntamos.

Para juntar la rama con la rama principal, nos situamos en la rama principal con

cvs udpate -A

Luego le decimos a cvs que nos haga un update de la rama mezclándolo que lo que tenemos fuera. Eso se hace con update y la opción -j

cvs update -j etiqueta_rama

Esto nos junta todos los ficheros. Debemos prestar atención a la salida para ver de qué ficheros se ha hecho "merge" (mezcla de las dos ramas). Debemos resolver todos esos "merge" a mano, es decir, editando el fichero con el editor y arreglandolo.

Finalmente, hacemos un commit de todos los cambios

cvs commit -m"Juntadas las ramas"

05 febrero 2006

Una pena de foro

Hay dos páginas con foros que suelo visitar con frecuencia: http://www.programacion.com y http://www.lawebdelprogramador.com, en concreto, suelo visitar los foros de C/C++ y de java.

Hay uno sin embargo que me ha decepcionado desde hace un tiempo, es el de C/C++ de http://www.programacion.com/foros/29. Se ha convertido en un foro de "quiero que alguien me haga los deberes" y parece más un listado de problemas de clase que un foro de programación.

Hay un par de post: "HEY SI ALGUIEN NECESITA ALGUN PROGRAMA" y "Tengo varios programas en C++" que llevan más de 500 posts y aparecen en la primera página del foro desde hace ya no sé cuánto tiempo.

Estaría bien, por un lado, que la gente se esforzara un poco con sus deberes de clase y no los pidieran hechos. En el 99% de los casos es inútil pedirlos (nadie contesta) y molestan a los que vamos a aprender y resolver dudas concretas.

Como esto es casi imposible, también estaría bien que los moderadores del foro pusieran límite a este tipo de cosas. Para no perder visitas, podrían poner un foro separado de "tareas de clase" y mover allí todos los posts de este estilo.

En fin, que desde que eso es así, prácticamente no visito ese foro.

03 febrero 2006

Hacer ramas en cvs

Encontré un tutorial de cvs avanzado en el que explica bastante bien el tema de hacer ramas: http://es.tldp.org/Manuales-LuCAS/doc-cvsbook-es/cvsbook-html/CVS-avanzado.html#CVS%20avanzado

Aprovechando en el trabajo que tuve que hacer un cambio más o menos importante, hice una rama y de paso probé todo el tema este.

Para hacer la rama, siguiendo el tutorial, hay que dar los siguientes pasos:
  • Marcar la versión actual con una etiqueta. No es obligatorio, pero de esta forma se puede obtener la versión de justo antes de hacer la nueva rama. Situándose lo más arriba posible en la estructura guardada en cvs, se ejecuta el comando
    cvs tag etiqueta_antes_rama
  • Luego se crea la rama, dándole una nueva etiqueta. El comando es
    cvs tag -b etiqueta_rama
  • Ya está creada. El que quiera trabajar en la nueva rama debe sacar esta versión con el comando
    cvs update -r etiqueta_rama
    Todo los commit y update que haga desde este momento, se harán en la rama nueva.
  • Para volver a la rama principal, se ejecuta el comando
    cvs update -A
    La opción -A saca los ficheros de la rama por defecto de CVS, que si no hemos hecho nada en contra, es la rama principal.
    No podemos hacer cvs update -r etiqueta_antes_rama porque nos sacaría justo los ficheros con esa etiqueta y no los más modernos de esa rama.
Podemos cambiar de rama siempre que nos interese. Me ha llamado la atención al hacerlo que si en una rama borro ficheros y en la otra no, los ficheros se me borran o recuperan según hago update de una rama o de otra.

Cuando en el trabajo junte ambas ramas y experimente con el tema, volveré a poner un post sobre cómo juntar ramas.

27 enero 2006

Variables de menos de un byte en C

En C es posible crear una estructura cuyos campos tengan menos de 1 byte de tamaño. Esto es útil, por ejemplo, si queremos muchos booleanos.

Cuando tenemos booleanos, lo normal es hacer un entero de valor 0 o 1. Si somos listos, quizás pongamos un char que ocupa menos.

Con algo como esto, podemos hacer una estructura con muchos booleanos dentro de un solo bit. La siguiente estructura, por ejemplo, contiene 5 booleanos de 1 bit.

struct Booleanos
{
unsigned int b1:1;
unsigned int b2:1;
unsigned int b3:1;
unsigned int b4:1;
unsigned int b5:1;
}


Posiblemente el compilador haga que la estructura completa ocupe uno o varios bytes completos (según la cantidad de campos que metamos dentro), pero hemos conseguido un ahorro importante de espacio de memoria.

Tambien es posible hacer campos de dos bits, de tres, etc.

struct VariableRaras
{
unsigned int a:3; /* 3 bits */
unsigned int b:4; /* 4 bits */
}


En estos campos sólo caben número pequeños. Por ejemplo, en a sólo podemos meter números de 0 (000 en binario) a 7 (111 en binario). Si nos pasamos de rango, estaremos machacando el campo anterior o el siguiente (depende del micro que usemos).

En fin, esto es útil para ahorrar espacio de memoria o incluso cuando trabajamos a nivel de bits, para acceder más fácilemte a bits o grupos de bits dentro de una zona de memoria.

En http://www.zator.com/Cpp/E4_6.htm hay un tutorial más completo sobre esto

26 enero 2006

Aplicaciones para windows y linux

En http://es.wikipedia.org/wiki/Equivalencias_Windows_en_Linux#Programaci.C3.B3n_y_Desarrollo hay una lista de aplicaciones para windows y linux.

Teóricamente hace una especie de relación entre una y otra, de forma que si conocemos una herramienta para windows, seamos capaces de ver qué herramientas similares hay en linux.

Aunque no estoy del todo de acuerdo con todas las equivalencias que muestra (hay herramientas que corren en ambos sistemas por ser java y sólo aparecen en uno), el enlace es interesante para conocer herramientas existentes. Hay aplicaciones de autocad, para manejo de cvs, IDEs de trabajo, compiladores para varios lenguajes, sistemas de informacion georeferenciada (mapas), etc.

25 enero 2006

Llamar a otros ant desde un ant

Desde un build.xml de ant podemos hacer llamadas a otros build.xml ejecutándose en un ant aparte. También podemos llamar a otros "target" del mismo build.xml

Para llamar a otro "target" que esté en el mismo build.xml, hacemos algo como esto

<target name="llamar_a_otro_target">
<antcall target="otro_target"/>
</target>


<target name="otro_target">
<!-- Aqui se hacen cosas-->
</target>


Para llamar a otro "target" que esté en otro build.xml, se hace así

<target name="llamar_a_otro_target_de_otro_build">
<ant antfile="/directorio/build.xml"
dir="/directorio"
target="otro_target"/>
</target>


Si no ponemos "target" en la llamada a ant, se ejecutará el "target" por defecto del build.xml

24 enero 2006

Ficheros de propiedades en ant

Hay una pequeña tontería de ant que he descubierto hace poco y me ha llamado la atención.

Consiste básicamente en que podemos poner en un fichero (que suelo llamar build.properties) una serie de propiedades al estilo de fichero de propiedades java, es decir, algo como esto

variable1=valor1
variable2=valor2

Luego, en ant, justo debajo del tag de project, podemos hacer que se lea este fichero de propiedades

<project basedir="." default="all" name="MiProyecto">
<property file="build.properties"/>
...
</project>

Con esto se leen las variables del fichero de propiedades y luego pueden usarse fácilmente en las tareas que tengamos definidas en el build.xml. Por ejemplo

<javac srcdir="${FUENTES}" destdir="${DIRECTORIO_CLASS}" ...>
...
</javac>

compilaría en java lo que encuentre en el directorio al que apunte la variable/propiedad FUENTES y dejaría los ficheros .class en el directorio al que apunte la variable/propiedad DIRECTORIO_CLASS.

23 enero 2006

Cuando me meten en una ventana

Con relativa frecuencia hago clases que heredan de JButton, por ejemplo, MiBoton. Esta clase MiBoton ya tiene implementado el código que debe ejecutarse al apretar el botón y es un código más o menos reutilizable en varios sitios (por eso el hacer el botón en concreto).

A veces ese botón necesita abrir una pequeña ventana secundaria (una ventana de confirmación, para introducir algunos datos o lo que sea). Esa ventana, para que todo cuadre como debe ser, debe ser un JDialog cuyo padre sea la ventana (JFrame o JDialog) que contiene al botón, de esta forma el dialogo que abramos no saldrá detrás de la ventana que contiene al botón. Y ahí viene el problema. Cuando se hace el código del botón más o menos genérico, no se dispone de la ventana que contiene el botón. De hecho, si el botón está en una especie de librería, no se sabe quién lo va a utilizar ni en qué ventana lo va a poner.

La solución que le he dado a este problema, es crear un HierarchyListener. El botón se suscribe a un cambio en la jerarquía de padres de él mismo. De esta forma, cuando el botón se añade a un panel o ese panel se añade a una ventana, el botón se entera. Con cada cambio de jerarquía, se intenta obtener la ventana padre del botón, utilizando el método SwingUtilities.getWindowAncestor(). En el momento que este método nos devuelva una ventana, esta será la ventana que contiene al botón y esta será la ventana padre del JDialog que va a desplegar el botón.

El código para todo esto puede estar en el constructor de MiBoton y es algo como esto

this.addHierarchyListener(new HierarchyListener( )
{
public void hierarchyChanged( HierarchyEvent e )
{
// Se mira si el panel ya ha sido añadido a alguna ventana
Component ventana = SwingUtilities.getWindowAncestor( EditorListaStrings.this );

if( ventana != null )
{
// Se elimna la suscripción para evitar construir la
// ventana dos veces
EditorListaStrings.this.removeHierarchyListener( this );
// Construye el dialogo usando como padre ventana
construyeDialogo (ventana);
}
}

} );

19 enero 2006

Enterarnos de que nuestro programa java termina

Tenemos la posibilidad de enterarnos de que nuestro programa java termina, bien por hacer System.exit(), bien por la pulsación de Ctrl-C. De esta forma, al producirse la salida, podemos realizar ciertas tareas antes de que termine la ejecución.

La forma es utilizar el método addShutdownHook() de la clase Runtime. A este método le pasamos un Thread que se arrancará cuando se produzca el exit() o la terminación por Ctrl-C.

El siguiente trocito de código hará que se escriba "Adios" al terminarse nuestro programa

Thread hilo = new Thread (new Runnable ()
{
public void run ()
{
System.out.println ("Adios");
}
});

Runtime.getRuntime().addShutdownHook(hilo);

Lo de que queramos enterarnos cuando se pulsa Ctrl-C es entendible. ¿Para qué sirve enterarnos de un exit(), si lo hacemos nosotros?. Tiene bastante sentido en clases que formen parte de una librería más o menos reutilizable. Si hacemos una clase común/reutilizable que necesite hacer algo cuando termina el programa, ya no dependemos de que el que la use nos llame a algún método antes de hacer exit(). Nuestra propia clase puede usar este sistema para asegurarse de ser llamada al terminar el programa java.

17 enero 2006

Patrón fachada

Supongamos que tenemos una librería gráfica similar a la que tengo en mi página.

En ella hay una clase Lienzo que es la zona de dibujo para dibujar. Hay una clase EscalaGrafica que es la encargada de hacer las conversiones de coordenadas deseadas para el gráfico a coordenadas de pixels de pantalla y viceversa. Esta clase se encarga, por ejemplo, de hacer las cuentas para que un Lienzo con 100 pixels de ancho tenga unos valores de x que van de -PI a +PI para dibujar una función sin(x). También hay una clase Rejilla para dibujar unos ejes y un cuadriculado. Hay una clase Mano para permitir arrastrar con el ratón un gráfico. Hay unas clases de botonería de zoom para poder hacer zoom en el gráfico. También hay unas clases Cursor que al pasear el ratón por encima del gráfico nos van diciendo las coordenadas del gráfico en las que estamos, aparte de permitir dejar unas marcas en el gráfico.

Ahora supongamos que vamos a hacer un programa matemático en el que tenemos una interface de usuario y en ella hay unos 200 gráficos. Todos ellos son de funciones matemáticas y queremos que tengan un aspecto similar. Todos pintarán una función matemática, llevarán una rejilla, unos botones de zoom y una mano que permita arrastrar el gráfico.

Para hacer cada uno de estos gráficos debemos hacer los siguientes pasos:
  • Instanciar la clase Lienzo.
  • Instanciar la clase EscalaGrafica que convierte coordenadas a pixels y meterla en la clase Lienzo.
  • Instanciar la clase Rejilla y meterla en Lienzo.
  • Instanciar la clase Mano y meterla en Lienzo.
  • Instanciar los botones de zoom, pasarles la EscalaGrafica y ponerlos en un contenedor junto al Lienzo.
  • Finalmente, el paso que es especifico de cada grafico, que es instanciar el ObjetoGrafico que representa la funcion matemática que queremos pintar y meterlo en el Lienzo.
Vemos que hay un montón de pasos que son comunes a todos los gráficos. Son comunes todos excepto el último, en el que la función será la que sea. También vemos que hay un pequeño berenjenal de clases e inicializaciones entre ellas.

El patrón fachada nos dice que podemos hacer una clase que nos oculte toda esta complejidad. La clase puede ser GraficoDeFuncion, que sea un contenedor (un JPanel java) que dentro instancie el Lienzo, la botonería de Zoom, la Mano, la EscalaGrafica y la Rejilla. Lo inicialice y lo coloque todo en su sitio. Únicamente habría que pasarle desde fuera la función matemática que queramos y si acaso los valores extremos de coordenadas (de -PI a PI para x y de -1 a 1 para y si queremos pintar un sin(x), por ejemplo).

De esta forma, en nuestra aplicación, iremos instanciando clases GraficoDeFuncion que son más fáciles de inicilizar.

En resumen, el patrón fachada consiste en crear una única clase de manejo más fácil que nos permita acceder a un conjunto más o menos numeroso y complicado de clases que necesitan una inicialización compleja. Quizás no nos deja hacer todo lo que se podría hacer con las clases sueltas (por ejemplo, no poner Mano a un gráfico o ponerle una botonería de zoom distinta a las demás), pero nos ahorra mucho el trabajo de instanciación e inicialización de todo eso.

15 enero 2006

iText : Una librería para pdf en java

iText es una librería gratuita que permite generar ficheros pdf desde código java. No la he probado, pero es una cosa a mirar si alguna vez hace falta...

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.