2010-10-18 30 views
5

Aquí está el fondo del problema subyacente, estoy colaborando con un grupo en un proyecto que usa las bibliotecas Swt y estoy tratando de empaquetar el software para la implementación. Como resultado, SWT depende de la plataforma/arquitectura. Me gustaría poder empaquetar los seis jar s (linux, mac, win y 32/64-bit) en el mismo paquete y usar la biblioteca apropiada según el sistema. Me doy cuenta de que es un desafío difícil, sin embargo, cambiar a Swing (o cualquier otra cosa) no es realmente una opción en este momento.Problemas con la carga de recursos durante la ejecución

He encontrado una serie de hilos relevantes (@Aaron Digulla's thread y @mchr's thread) que me proporcionaron información valiosa sobre el problema en cuestión. He tratado de implementar la solución propuesta por @Alexey Romanov here. Con una diferencia, ya que el método loadSwtJar() que él propone no es estático, instanciar el objeto, e inmediatamente después, ejecutar el método antes de hacer cualquier otra cosa al objeto.

Parece que el procedimiento de carga no funciona correctamente. Mi razonamiento para esta declaración es el siguiente:

  • Si todos Swt frascos se retiran de la ruta de clase del archivo jar ejecutable, Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/events/MouseListener se lanza la cual es causada por: java.lang.ClassNotFoundException: org.eclipse.swt.events.MouseListener

para mí este medio que las bibliotecas no se encuentran en classpath, ¿me equivoco?

  • Si swt jarras quedan en el classpath entonces el primer archivo jar es utilizado por el sistema durante la ejecución. Es decir, si gtk-linux-x86_64 es el primer jar swt en la lista de jar, entonces el sistema intenta usarlo, independientemente de si el sistema es win32 o Mac OSX.

He intentado añadir un poco de salida para ver si el método loadSwtJar() es la elección de la jarra de la derecha, y la salida parece correcto en todas las plataformas que he tratado, al igual que en el paquete adecuado se selecciona (y deben existir los archivos en el jar ejecutable). Sin embargo, la biblioteca correcta no está cargada, se producen errores de ejecución: Exception in thread "main" java.lang.reflect.InvocationTargetException causada por, por ejemplo, Caused by: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM (Tenga en cuenta que este es el error que obtengo en mi máquina Linux si cambio el orden de aparición de 64 bits y 32 bit swt bibliotecas en el archivo build.xml)

Entonces, ¿cuál parece ser el problema aquí? ¿Me estoy perdiendo algunos detalles, o simplemente no es posible verificar las propiedades del sistema y cargar una biblioteca apropiada en consecuencia?

Por último, a continuación se muestra un fragmento de mi archivo de compilación, que figura que podría ayudar a encontrar el origen del problema.

Gracias de antemano,


EDIT: Después de una larga sesión de depuración con un colega, el problema se resuelve (excepto un molesto error con respecto a la gestión de rosca en MacOS como he mencionado here). Implicaba retocar con la creación de ANT, así como la forma en que se escribió la clase principal.(La clase principal, como resultado, estaba extendiendo & implementando referencias de la biblioteca SWT lo que significaba que el código no compilaría en absoluto, envolvió la clase principal con otra clase y cargó las jarras SWT de allí que parecían ser suficientes para abordar el problema)

Gracias y saludos a todos los que contribuyeron, especialmente @Aaron. ¡Muy apreciado!

Respuesta

5

Aquí hay una copia de la última versión de mi clase principal. Avísame si eso funciona para ti. Lo probé en Linux (32/64 bits) y Windows (32 bits).

package de.pdark.epen.editor; 

import java.io.File; 
import java.lang.reflect.Constructor; 
import java.lang.reflect.Method; 
import java.net.URL; 
import java.net.URLClassLoader; 

import org.apache.commons.lang.SystemUtils; 
import org.apache.commons.lang.exception.ExceptionUtils; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 

import ch.qos.logback.classic.LoggerContext; 
import ch.qos.logback.core.util.StatusPrinter; 
import de.pdark.epen.exceptions.WikiException; 

public class Main 
{ 
    public final static String VERSION = "V0.9 (13.05.2010)"; //$NON-NLS-1$ 
    private final static Logger log = LoggerFactory.getLogger (Main.class); 

    private static final String ORG_ECLIPSE_SWT_WIDGETS_SHELL = "org.eclipse.swt.widgets.Shell"; //$NON-NLS-1$ 

    /** 
    * @param args 
    */ 
    @SuppressWarnings({"nls", "PMD.SystemPrintln"}) 
    public static void main (String[] args) 
    { 
     String msg = "Starting ePen "+VERSION; 
     System.out.println (msg); 
     log.info (msg); 

     LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory(); 
     StatusPrinter.print (lc); 

     int rc = 1; 
     try 
     { 
      Main main = new Main(); 
      main.run (args); 
      rc = 0; 
     } 
     catch (Throwable t) //NOPMD 
     { 
      ExceptionUtils.printRootCauseStackTrace (t); 
     } 
     finally 
     { 
      System.out.println ("Done."); 
      log.info ("Exit {}", rc); 
      System.exit (rc); //NOPMD 
     } 
    } 

    @SuppressWarnings({"nls", "PMD.SystemPrintln", "PMD.SignatureDeclareThrowsException"}) 
    private void run (String[] args) throws Exception 
    { 
     if (!SystemUtils.isJavaVersionAtLeast (150)) 
     { 
      System.out.println ("Version="+SystemUtils.JAVA_VERSION_INT); 
      throw new WikiException ("Need at least Java 5 but this Java is only "+SystemUtils.JAVA_VERSION); 
     } 

     loadSwtJar(); 

     URLClassLoader cl = (URLClassLoader) getClass().getClassLoader(); //NOPMD 
     Class<?> c = cl.loadClass ("de.pdark.epen.editor.EPenEditor"); 
     Class<?> shellClass = cl.loadClass (ORG_ECLIPSE_SWT_WIDGETS_SHELL); 

     Constructor<?> ctor = c.getConstructor (shellClass); 
     Object obj = ctor.newInstance (new Object[] { null }); 
     Method run = c.getMethod ("run", args.getClass()); //$NON-NLS-1$ 
     run.invoke (obj, new Object[] { args }); 
    } 

    @SuppressWarnings({"nls", "PMD"}) 
    private void loadSwtJar() 
    { 
     try { 
      Class.forName (ORG_ECLIPSE_SWT_WIDGETS_SHELL); 
      // Already on classpath 
      return; 
     } catch (ClassNotFoundException e) { 
      // Add the JAR 
     } 

     String osName = SystemUtils.OS_NAME.toLowerCase(); 
     String osArch = SystemUtils.OS_ARCH.toLowerCase(); 

     String swtFileNameOsPart = 
      osName.contains("win") ? "win32" : 
      osName.contains("mac") ? "macosx" : 
      osName.contains("linux") || osName.contains("nix") ? "linux" : 
      null; 
     String swtFileNameUiPart = 
      osName.contains("win") ? "win32" : 
      osName.contains("mac") ? "cocoa" : 
      osName.contains("linux") || osName.contains("nix") ? "gtk" : 
      null; 

     if (null == swtFileNameOsPart) 
     { 
      throw new RuntimeException ("Can't determine name of SWT Jar from os.name=[" + osName + "] and os.arch=[" 
        + osArch + "]"); 
     } 

     String swtFileNameArchPart = osArch.contains ("64") ? ".x86_64" : ".x86"; 
     if(".x86".equals(swtFileNameArchPart) && "macosx".equals(swtFileNameOsPart)) { 
      swtFileNameArchPart = ""; 
     } 

     String swtFileName = "org.eclipse.swt." + swtFileNameUiPart + "." + swtFileNameOsPart + swtFileNameArchPart + "-3.6.0.jar"; 
     File file = new File ("swt", swtFileName); 
     if (!file.exists()) 
     { 
      throw new RuntimeException ("Can't locate SWT Jar " + file.getAbsolutePath()); 
     } 
     try 
     { 
      URLClassLoader classLoader = (URLClassLoader) getClass().getClassLoader(); 
      Method addUrlMethod = URLClassLoader.class.getDeclaredMethod ("addURL", URL.class); 
      addUrlMethod.setAccessible (true); 

      URL swtFileUrl = file.toURI().toURL(); 
      log.info ("Adding {} to the classpath", swtFileUrl); 
      addUrlMethod.invoke (classLoader, swtFileUrl); 
     } 
     catch (Exception e) 
     { 
      throw new RuntimeException ("Unable to add the swt jar to the class path: " + file.getAbsoluteFile(), e); 
     } 
    } 
} 
+0

Gracias Aaron. Tengo que decir que no estoy muy seguro de cómo cavar para encontrar los archivos swt, ¿tiene sus archivos Swt dentro de un gran contenedor con todas las otras dependencias o de alguna otra manera? – posdef

+0

@ Aaron: una pregunta más, ¿ha tenido algún problema con los paquetes SWT sin firmar? o solo soy yo ... – posdef

+0

@posdef: pongo todos los JAR normales en un directorio llamado 'lib /' y los seis JARs SWT en 'swt /'. Si los coloca en un solo directorio, es probable que el classpath del manifiesto principal de JAR intente cargar el incorrecto. –

0

Puede utilizar Java Web Start como mecanismo de arranque para su aplicación SWT multiplataforma. Vea un corresponding entry en SWT FAQ.

Como alternativa, podría colocar las bibliotecas nativas de SWT para cada plataforma en carpetas separadas y especificarlas como -Djava.library.path en el script de inicio específico de la plataforma.

+0

He visto la entrada SWT FAQ, y esperaba evitar eso, en otras palabras, tenía 6 archivos diferentes para administrar. Pero supongo que tendrá que ser el plan B si esto no funciona al final. Por cierto; Solo para aclarar, las preguntas frecuentes sugieren separar las bibliotecas de SWT del resto de las dependencias e incluirlas en el momento de la ejecución del archivo JNLP, ¿es así? – posdef

+0

Correcto, el cargador JNLP se ocupará de cargar libs y jar específicos de la plataforma. –

+0

He estado buscando más en este ejemplo como un plan de copia de seguridad, y tengo que decir que es bastante sospechoso que no se hayan incluido en el ejemplo (incluso SunOS está incluido). Además, ¿hay una lista completa de posibles valores de retorno para las llamadas ''? – posdef

Cuestiones relacionadas