29 octubre 2005

Más de emule

Pues nada, lo del emule ni patrás.

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

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

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

27 octubre 2005

Una de eMule

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

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

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

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

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

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

26 octubre 2005

Captura de teclas en un JTextField de java

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

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

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

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

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

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

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

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

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

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

25 octubre 2005

Uso de strtok()

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

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

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

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

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

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

Lo mejor es meter todo en un bucle

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

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

23 octubre 2005

El patrón composite

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

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

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

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

El código puede ser parecido a este

class CompuestoEscritores implements Escritor
{

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

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

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

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

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

22 octubre 2005

Por qué usar interfaces

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

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

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

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

Nos ponemos manos a la obra y cambiamos nuestro programa

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

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

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

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

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

double valor2 = this.otrasCuentas(datos);

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

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

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

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

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

Nuestro código queda así de bonito ahora

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

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

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

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

Un buscador de rpm

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

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

19 octubre 2005

La clase java.awt.Robot

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

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

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

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

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

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

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

plugin de vi para eclipse

Llevo muchos años usando el vi y para mi es uno de los editores más potentes y que permite hacer las cosas más rápidamente. Cuesta al principio, pero cuando se conoce le da mil vueltas a cualquier editor de windows.

La única ventaja que veo a los editores de los entornos de desarrollo como eclipse o netbeans, es la posibilidad de autocompletar los nombres de métodos y clase al programar, ver la ayuda sobre la marcha, los parámetros que hay que poner en los métodos, etc, etc. Sin embargo, a la hora de reorganizar el código, mover lineas de un lado a otro, cambiarlas, buscar, etc, etc, echo mucho de menos el vi. De hecho, en estos editores me encuentro "atado" y con falta de opciones, usar el ratón para hacer cosas como seleccionar me parece lento, ...

Por ello, me he decidido a buscar un plugin de eclipse para ver si se el editor se puede comportar como el vi.

Primero encontré viPlugin, pero es de pago. Se instala, funciona bien, pero cada poco sale un popup indicando que la versión es de prueba y que debemos pagar unos 15 euros.

Rebuscando más, vi que existe una versión más antigua de viPlugin en sourceforge, gratuita. La instalé y tiene una pequeña pega. Cuando abro un fichero el viPlugin funciona fatal. Da igual como mueva el cursor, que va dando saltos por la pantalla y acaba llegando al final de fichero sin posibilidad de moverse. Lo más curioso es que si formateo el código (con jalopy o con la opción de formateo de código de eclipse) entonces ya sí funciona bien. De todas formas, cerrando y volviendo a abrir el código ya formateado, vuelve a ir mal. Es necesario darle a formatear otra vez.

Todo esto me hace pensar que puede haber algún problema con los retornos de carro (trabajo en windows y el vi es por excelencia de unix, donde los caracteres de fin de línea son distintos) o con algún otro tema del fuente, como tipo de caracter (ascii, us-ascii, utf-80, etc).

En fin, intentaré ver si encuentro el error o seguiré buscando más plugins.

18 octubre 2005

Sobre Debian

En http://www.mononeurona.org/index.php?secmenu=23 hay una página con un montón de tutoriales bsatante interesantes y muy claros sobre cosas de debian/linux.

Habla sobre todo de instalaciones y configuraciones de cosas sobre linux, como apache, mysql, php, informix, correo, etc.

17 octubre 2005

Me han fastidiado los de oracle

En el trabajo tenemos y tratamos de mejorar un conjunto de clases java para facilitar los programas que manejan base de datos. Básicamente la idea que tenemos es la siguiente:

Para cada posible tipo de campo de la base de datos tenemos un editor, es decir tenemos varios editores capaces de manejar los datos que nos devuelve un resultSet.getObject(): String, BigDecimal, Double, etc, etc. Todos esos editores implementan una interface común de tomaDatos() y dameDatos(). Estos editores se pueden configurar con las restricciones de los campos en la base de datos (número de cifras, de decimales, es obligatorio, es editable, etc) que se pueden leer automáticamente con los metadatos desde java.

Tenemos una clase que lee un fichero de configuración. Ese fichero contiene los nombres de las tablas de base de datos y el tipo de editor que debemos usar para ese campo, con sus restricciones. Ese fichero se genera automáticamente con un programita java que nos hemos hecho y que consulta los metadatos de la base de datos. Hemos optado por este fichero de configuración por no tener que ir leyendo en tiempo de ejecución los metadatos de la base de datos.

Tenemos también un panel al que se le pasan nombres de campos de la base de datos. El panel consulta el fichero de configuracion (que también se le pasa), instancia el editor adecuado para ese campo, lo configura con sus restricciones y lo coloca en el panel. Con esta clase creamos todos los formularios de forma casi automática.

El panel tienen dos metodos tomaDatos() y dameDatos() que reciben y dan un Hashtable. La clave del Hashtable es el nombre del campo de la base de datos y el valor es el valor que nos devuelve el ResultSet. De esta forma, rellenando el formulario y llamando al dameDatos(), obtenemos un Hashtable con todos los campos del formulario rellenos. El tomaDatos() lo que hace es repartir los valores del Hashtable a los editores individuales que hay dentro del panel.

Por supuesto, tenemos un TableModel que en cada fila admite uno de estos Hashtables y un JTable al que dándole los nombres de los campos que queremos que se vean, añade las columnas correspondientes.

Finalmente, tenemos una clase a la que dándole un String con un select de oracle, es capaz de realizar la consulta y construirnos los Hashtables correspondientes. Esta clase, si le damos cualquier sentencia SQL con nombres de campos entre # y un Hashtable, reemplaza los nombres de campos entre # por los valores del Hashtable. De esta forma se construyen los insert, delete, update e incluso los where de los select de forma más o menos automática. Por ejemplo, si hacemos "select * from tabla where campo=#campo#" y le pasamos un Hashtable con una clave "campo" y con valor Integer=3, esta clase reemplaza el #campo# por un 3. El select quedaría asi "select * from tabla where campo=3".

Con todo esto, para hacer un programa que trabaje con base de datos, creamos un fichero de configuración con nuestro programa independiente. Escribimos los select, insert,update en un fichero de configuracion. Los leemos y lo pasamos a nuestra clase de consultas, que nos devuelve directamente Hashtables. Estos los podemos meter tal cual en una tabla o en un formulario que se construyen solos con un par de líneas de código. De la misma forma, leyendo datos nuevos de un formulario y reemplazando campos entre # en el insert correspondiente, podemos insertar.

¿Cual es el problema?. Para construir el Hashtable con el resultset que obtenemos del select de SQL, necesitamos leer los metadatos del ResultSet. Estos metadatos sólo nos devuelven el nombre del campo, pero no de la tabla ni del esquema al que pertenece dicho campo ( ver última línea en http://www.oracle.com/technology/sample_code/tech/java/codesnippet/jdbc/OracleResultSetMetaData.html). Esto nos plantea el problema que si el select pide dos campos con el mismo nombre en distintas tablas, a través de los metadatos del ResultSet no podemos distinguir qué columna es qué campo. No queda más remedio que hacer los select con cosas como "select tabla1.nombre as nombre_1, tabla2.nombre as nombre_2 ...", que no deja de ser un incordio, puesto que hay que acordarse de hacerlo y no es algo que nos haga el código automáticamente.

_gxx_personality_v0

En varias ocasiones me han llegado correos de gente que quieren correr algunos de los ejemplos de mi página web y tienen problemas. El problema suele un error del estilo

undefined reference to '_gxx_personality_v0'

El problema no es del ejemplo. No sé muy bien a qué se debe, pero da la impresión de que en algunas versiones de linux han hecho depender el compilador de C (el gcc) de alguna librería propia del de C++ (el g++).

Buscando por internet (_gxx_personality_v0 en google) he visto que hay montones de entradas en los foros referenciando este error.

Las soluciones que se proponen parecen ser:
  • Usar g++ en vez de gcc al compilar, aunque sea un programa de C en vez de C++
  • Añadir a mano el linkado con la libreria /usr/lib/libstdc++.so. Bastaría con añadir una opción -lstdc++ en la línea de compilado con gcc.
Con esto, en teoría estaría resuelto el problema. Parece, de todas formas, que en versiones más modernas de linux se ha corregido el error.

En http://mapserver.gis.umn.edu/data2/wilma/mapserver-users/0303/msg00377.html hay un mensaje de un foro en el que básicamente explica estas dos soluciones.



16 octubre 2005

DVD2DivX V4

En DVD2DivX V4 hay una página interesante en la que cuenta, además de poder descargar todo lo necesario, cómo pasar una película DVD a formato DivX.

Grabando con la AverTV 203

Por fin me he puesto a grabar algo un poco más en serio con la AverTV 203.

Me gusta más que la Pinnacle PC TV Pro. Esta me daba algún problema cuando la señal no era buena. Dejaba de grabar sin más. Además, no permitía grabar en cualquier formato, simplemente grababa en mpeg-1 y ya está (la opción de mpeg-II es de pago aparte).

La Aver TV me da también algún problema, pero es secundario. Mientras está grabando, se me ha quedado colgada la imagen. Sin embargo, sigue grabando y la grabación luego está bien. También alguna vez en el arranque da un error. Hay que matar la aplicación y volverla a arrancar. Lo mejor de todo es que es capaz de grabar con cualquier codec que tengamos instalado, por lo que es posible grabar directamente en divX o cualquier otro. También soporta además los formatos de Video CD, Super Video CD y DVD.

13 octubre 2005

Un chat en java

En http://www.geocities.com/xtr3m3_sc0rpi0/java/chat/ hay el código de ejemplo de un chat en java. No lo he probado, pero tiene pinta de ser un código sencillo y que sirve perfectamente de ejemplo.

09 octubre 2005

Entornos y herramientas para desarrollo de software

En Entornos y herramientas para desarrollo de software hay un montón de enlaces a herramientas de programación:

- Herramientas case, para dibujo uml
- Editores configurables
- Herramientas para "navegar" por el código
- Compiladores varios
- Análisis de código (métricas y demás)
- Cosas de ambiente unix sobre windows

En fin, un poco de todas aquellas cosas que en algún momento hemos necesitado y que hemos tenido que buscar como hemos podido.

05 octubre 2005

El fin de fichero en C

Una pequeña tontería que suele despistar al principio.

Cuando leemos un fichero en C, normalmente leemos hasta que encontramos el fin de fichero, con la función feof(). Lo normal es hacer un bucle de este estilo

while (!feof(fichero))
{
...
}

El problema que no todo el mundo sabe es que feof() es cierto sólo cuando intentamos leer después de acabar el fichero.

Si el fichero está vacío y hacemos esto

fichero = fopen (...);
while (!feof(fichero))
{
fread (...);
tratarLeido(...);
}

abrimos el fichero. Como no hemos intentado leer, feof() es false y leemos datos. No hay datos que leer, fread() devolverá un error (que solemos ignorar) y tratamos los datos que no hemos leidos. En el siguiente bucle, como ya hemos intentado leer después de fichero, feof() es true y se acaba el bucle.

Si hubiera datos, trataríamos todo bien, pero después de leer los últimos datos y tratarlos, feof() sigue siendo false. Intentamos una nueva lectura fallida y tratamos los datos que no hemos leido.

La forma correcta de hacer este bucle, es hacer una lectura ANTES de mirar la condición feof(). Es decir

fichero = fopen(...);
fread (...)
while (!feof(fichero))
{
tratarLeido(...);
fread(...);
}

De esta forma, inmediatamente después de leer se hace la comprobación de si hemos obtenido el feof() y si no es así, tratamos el dato.

04 octubre 2005

Las cosas de Windows

Con el cuento de la tarjeta aver media, tuve que reinstalar windows desde cero. Como tenía prisa por ver si se me repetía el problema, no di de alta ningún usuario.

Más adelante, resuelto el tema de la tarjeta, me decidí a dar de alta algunos usuarios (el de jugar con internet, el de hacer programas, etc). Todo decidido, le doy a "inicio", "configuracion", "panel de control", "usuarios y contraseñas" y me creo los usuarios.

¡Sorpresa!. Cuando intento entrar como uno de los usuarios recien creados, no puedo. Da error de que no se puede copiar la configuración local en c:\Documents and Settings\usuario. Veo que dicho directorio no se ha creado. Creándolo a mano, se obtienen nuevos errores.

Me voy a consultar internet y resulta que los usuarios se dan de alta en el icono de "Mi PC", con el botón derecho del ratón, sacamos el menú y le damos a "Administrar" y "usuarios locales y grupos", "usuarios", nuevamente botón derecho y "nuevo usuario". Haciéndolo así, funciona todo correctamente.

¿Para que sirve el "usuarios y contraseñas" del panel de control? Misterio.

03 octubre 2005

Meter todo nuestro programa java en un solo jar

A veces hacemos un programa java que tira de otros jar externos. Por ejemplo, si hacemos un programa de base de datos contra oracle, necesitamos el jar con el driver de oracle, es decir, el ojdbc14.jar. También podemos usar para sacar nuestros mensajes de error el log4j, y necesitamos el jar correspondiente log4j.jar

Cuando metemos nuestro programa en un jar, en nuestro fichero de manifiesto podemos poner que necesitamos esos jar externos. Sería algo como esto

Manifest-Version: 1.0
Class-Path: ./ojdbc14.jar ./log4j.jar
Main-Class: MiPaquete.MiClasePrincipal


Esto va más o menos bien. El problema es que junto con nuestro jar debemos entregar los otros jar. Es decir, nuestra aplicación está compuesta de tres jar, el nuestro, el de la base de datos y el del logger.

Hay una librería de sourceforge que permite empaquetar esos jar externos dentro de nuestro jar. De esta forma, dentro de nuestro jar meteríamos también el ojdbc14.jar y el log4j.jar, junto con nuestras clases.

De esta forma, nuestra aplicación se entregaría en un único MiPrograma.jar. Todos los demás jar necesarios estaría dentro.

Aunque no lo he probado todavía, parece que simplemente hay que bajarse el one-jar-boot.jar. Al desempaquetarlo salen varias clases y un fichero de manifiesto boot-manifest.mf. Ahora sólo tenemos que añadir a nuestro jar esas clases y ese nuevo fichero de manifiesto. Con ello, nuestro jar buscará los jar que indiquemos en el atributo "Class-Path" de nuestro fichero de manifiesto original, dentro del jar y no en el disco duro.