miércoles, 2 de septiembre de 2009

Runtime.exec con espacios en blanco para mysqldump

En esta entrada vamos a tratar un tema que me ha llevado varios días de cabeza. Les comento un poco para que se pongan en situación.
Estoy terminando una aplicación para la gestión de una clínica, es por eso junto con que era verano por lo que me he cogido un largo descanso con el blog, y de vuelta al cole me he encontrado con que necesitaba realizar copias de seguridad desde mi aplicación y a poder ser automáticamente, para que el cliente solo deba indicar la carpeta de destino. La aplicación la he realizado con Java y trabaja contra una base de datos MySQL, ya se que con el MySQLAdministrator es muy facil hacer copias pero los clientes solo quieren tener cosas practicas.
Googleando un poco pude comprobar que simplemente necesitaba ejecutar una serie de comandos junto con el PATH del fichero desde el metodo exec de Runtime:
--> Runtime().getRuntime().exec("mysqldump -h HOST -u ...-r PATH_FICHERO")

¿¿¿Cual ha sido mi sorpresa??? Pues cuando ya funcionaba correctamente y conseguía almacenar las copias de seguridad descubrí que si el directorio tenia espacios en blanco los comandos que recibia mysqldump se desordenaban después de la variable que contenía el PATH.
El problema reside en que el metodo exec(string) realiza una llamada a la clase StringTokenizer para obtener un vector con cada uno de los parametros, para ejecutar el metodo exec(nom_comando, vector_parametros[]). Este vector se compone de todas las palabras del String que esten separadas por espacios en blanco y por lo tanto si el PATH contiene alguno lo divide en dos partes como si la segunda fuera otro parámetro nuevo.
Despues de varios intentos intentando acertar como insertar los parámetros para que funcionase correctamente decidí implementar mi solución:

Primero, identificar que posición ocupa la variable que almacenara el PATH con posibles espacios. En mi caso ocupa el último lugar: dest.getCanonicalPath() (/home/pato_donald/copia seguridad)

String cnd = "mysqldump -h" + host + " -u" + user + " -P" + puerto + " -p" + pass + " -B base_datos" +"-r"+ dest.getCanonicalPath();

Segundo, convertir la cadena en un vector de tokens

StringTokenizer st = new StringTokenizer(cnd);

Tercero, realizar un bucle para que añada los tokens en el vector de Strings[] que debe recibir "exec".

int i=0;
while (st.hasMoreTokens()) {
coma[i]=st.nextToken() ;
System.out.println(coma[i] +" n:" + i);
i++;
}
Cuarto y final, modifica la posición del vector que debe contener el PATH con el espacio en blanco.

coma[7]= "-r"+ dest.getCanonicalPath();


String cnd = "mysqldump -h" + host + " -u" + user + " -P" + puerto + " -p" + pass + " -B base_datos" +"-r"+ dest.getCanonicalPath();
StringTokenizer st = new StringTokenizer(cnd);
String[] coma = new String[st.countTokens()+1];
int i=0;
while (st.hasMoreTokens()) {
coma[i]=st.nextToken() ;
System.out.println(coma[i] +" n:" + i);
i++;
}
coma[7]= "-r"+ dest.getCanonicalPath();

Process dumpProcess = Runtime.getRuntime().exec(coma);

Despues de ejecutar esto, el resultado del println sería:
mysqldump
-h127.0.0.1
-uUSER
-P3306
-pPASSWORD
-B
base_datos
-r/home/pato_donald/copias seguridad

Y la ejecución del metodo "exec" se realizaría correctamente sin la alteración de los paramteros.

Espero que les sirva de ayuda tanto como me ha servido a mí.

Un saludo ;)