miércoles, octubre 11, 2006

Abrir desde Java aplicación asociada por defecto para un fichero o URL (en Windows/Linux/MacOS)

Cuando programas aplicaciones para entornos de escritorio es común tener que abrir aplicaciones externas que estén asociadas con un determinado tipo de archivos. Es muy común tener que abrir una página web en el navegador, una imagen en el visor de imágenes del sistema operativo, etc. También es una situación bastante común lanzar el reproductor de audio/video del sistema operativo para no depender del JMF para reproducir un archivo multimedia (si alguien ha sufrido el JMF entenderá lo que estoy diciendo).

Esta tarea que a priori debería ser sencilla, en la realidad es una auténtica pesadilla en Java. El problema es que no existe ningún mecanismo estándar para averiguar cual es la aplicación asociada para un determinado tipo de archivo, por lo que normalmente hay que diseñar una solución muy específica que suele hacer que esa aplicación dependa de un programa externo vinculado a un sistema operativo.

La cosa se vuelve más complicada si pretendemos que esta solución sea multiplataforma. No existen muchos proyectos que ofrezcan soluciones generales para este problema, el único que ofrece una solución multiplataforma para este problema es jdic. Este proyecto usa JNI y depende de librerías nativas, lo que complica bastante el despliegue de la aplicación, por lo que a veces no es posible usarlo.

En entornos windows parece bastante arrancar la aplicación asociada a un archivo o URL, ya que existe el comando start que permite arrancar la aplicación asociada a un fichero o url. La única dificultad radica en diferenciar entre Windows 95 y las versiones actuales de Windows para ejecutar el intérprete de comandos adecuado. Existen otras soluciones más elaboradas, como puede ser averigual cuál es la aplicación asociada en el registro, pero creo que simplemente añaden complejidad al asunto y no aportan ninguna mejora.

En el caso de Linux la cosa se complica bastante, ya que no existe ningún registro centralizado de los programas asociados a un tipo de archivo o URL, por lo que cada entorno de escritorio ha solucionado este problema de formas distintas. Existe un subproyecto de FreeDesktop denominado Portland, que dentro de su paquete de utilidades xdg-utils incluye la aplicación xdg-open que permite solucionar el problema en Linux. Actualmente no está integrado dentro de todas las distribuciones, pero cuando lo esté xdg-open será un equivalente en Linux a su homólogo start en Windows.

Actualmente y a la espera de que se incluya el paguete xdg-utils en todas las distribuciones, la mejor opción es intentar detectar el entorno de escritorio que en el que se está ejecutando nuestra aplicación en Java, y en función de esto usar el programa específico para ese entorno de escritorio que nos proporcione la aplicación asociada. En el caso de GNOME tenemos la aplicación gnome-open y en el caso de KDE tenemos kmfclient.

Podemos encontrar un resumen de las opciones disponibles en Windows y Linux en este documento. También podemos ver ejemplos de uso de estas aplicaciones en Java en [1] y [2].

Con toda esta información he intentado crear una clase que recoge todas las ideas antes expuestas y que pretende solucionar el problema en el abanico más amplio de plataformas posible sin depender de librerías o programas externos.

Me gustaría compartir este código (que es tan sólo una primera versión) y que me comentaseis qué os parece y qué cosas cambiaríais/mejoraríais.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;

public class ApplicationLauncher {

public static void main(String args[]){
if(args.length!=1) {
System.out.println("Número de parámetros erróneo forma de uso: java -cp . ApplicationLauncher fichero|URL");
return;
}
if(args[0].indexOf("http")!=-1) launchURL(args[0]);
else launchDefaultViewer(args[0]);
}

private static String linuxDesktop = null;

private static String getEnv(String envvar){
try{
Process p = Runtime.getRuntime().exec("/bin/sh echo $"+envvar);
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String value = br.readLine();
if(value==null) return "";
else return value.trim();
}
catch(Exception error){
return "";
}
}

private static String getLinuxDesktop(){
//sólo se averigua el entorno de escritorio una vez, después se almacena en la variable estática
if(linuxDesktop!=null) return linuxDesktop;
if(!getEnv("KDE_FULL_SESSION").equals("") || !getEnv("KDE_MULTIHEAD").equals("")){
linuxDesktop="kde";
}
else if(!getEnv("GNOME_DESKTOP_SESSION_ID").equals("") || !getEnv("GNOME_KEYRING_SOCKET").equals("")){
linuxDesktop="gnome";
}
else linuxDesktop="";

return linuxDesktop;
}

public static Process launchURL(String url){
try{
if (System.getProperty("os.name").toUpperCase().indexOf("95") != -1)
return Runtime.getRuntime().exec( new String[]{"command.com", "/C", "start", url} );
if (System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") != -1)
return Runtime.getRuntime().exec( new String[]{"cmd.exe", "/C", "start", url} );
if (System.getProperty("os.name").toUpperCase().indexOf("MAC") != -1)
return Runtime.getRuntime().exec( new String[]{"open", url} );
if (System.getProperty("os.name").toUpperCase().indexOf("LINUX") != -1 ) {
if(getLinuxDesktop().equals("kde"))
return Runtime.getRuntime().exec( new String[]{"kfmclient", "exec", url} );
else
return Runtime.getRuntime().exec( new String[]{"gnome-open", url} );
}
}
catch(IOException ioex){System.out.println(ioex);}

return null;
}

public static Process launchDefaultViewer(String filepath){
return launchURL( new File(filepath).getAbsolutePath());
}

}

Del.icio.us Meneame

11 Comments:

Blogger Reinaldo dijo...

Hola...! me gustaria saber si has tenido experiencia porgramando con java y MapInfo, la verdad necesito saber todo acerca de esto. No h encontrado información que sea de mucha utilidad. No quiero aplicarle applets a mi aplicación porque me llevará mucho tiempo y tampoco quiero algo taaaaaaaan elaborado. Si está dentro de tus posibilidades ayudarme lo agradeceria mucho.!!!

noviembre 22, 2006 6:59 p. m.  
Anonymous Anónimo dijo...

hay un pequeño problema en windows con los documentos que tienen espacios, para ello tenemos que establecer el título de la ventana para después poner el nombre del fichero tal que:


exec( new String[]{"cmd.exe", "/C", "start", "\"dummy\"", strComando} );


el comando start se describe aqui:http://www.ss64.com/nt/start.html


Héctor

febrero 02, 2007 8:40 p. m.  
Anonymous Anónimo dijo...

Gracias hfelici!!

Parece ser que el comando start recibe como primer parámetro opcional el título de la ventana. El problema es que si el documento tiene espacios en blanco el comando puede interpretar la primera parte como el título de la ventana, lo que provocará que no se abra el documento correctamente.

Para estar seguros es mejor pasar siempre un primer parámetro una cadena de texto fija que sea el nombre de la ventana.

febrero 02, 2007 10:13 p. m.  
Blogger Víctor Manuel Ares Piñeiro dijo...

Muchísimas gracias por tu código, lo he modificado algo para adaptarlo a mis necesidades y funciona perfectamente. Gracias a ti y a tu código, me he decidido a continuar programando en Java una aplicación multimedia que tengo entre manos, después de plantearme seriamente el abandonar el lenguaje al comprobar el problema que tiene Java con las aplicaciones independientes que no pueden ser implementadas en applets. Muchísimas gracias de nuevo. Un saludo.

agosto 18, 2007 1:44 p. m.  
Anonymous Anónimo dijo...

GRacias por tu código, me ha sido de gran utilidad. Parece mentira que los de java no se procupen por cosas tan cotidianas como esta.

agosto 30, 2007 11:45 a. m.  
Blogger erior1 dijo...

Muchas gracias x el codigo hfelici...
con ese codigo pude abrir el visor de imagemes en windows xp...

julio 04, 2008 6:24 a. m.  
Blogger Stormrider of Theli dijo...

Genial lo del Application Launcher, funciona perfectamente. Muchas gracias.

noviembre 23, 2009 6:14 p. m.  
Anonymous Anónimo dijo...

Gracias, estoy empezando en java y me ha venido genial!!!
Saludos.

junio 01, 2010 12:25 a. m.  
Blogger teyin dijo...

Este post me ha sido de muchísima ayuda... humildemente no quería despedirme sin antes agradecerte la gran ayuda que me has dado.

Siempre se aprende algo nuevo... Gracias.

julio 14, 2010 8:59 p. m.  
Blogger BiosDev dijo...

Muchísimas gracias, me ha resultado muy útil. Enhorabuena por tu blog que me parece fantástico.

noviembre 02, 2011 3:15 p. m.  
Blogger darthmanuel dijo...

Hola a todos, logro abrir el visualizador de imágenes de windows lo que necesito, es darle las dimensiones y posición en donde aparezca el visulizador.

diciembre 21, 2011 3:19 p. m.  

Publicar un comentario

<< Home