2010-10-18 23 views
5

Voici l'arrière-plan du problème sous-jacent, je collabore avec un groupe sur un projet qui utilise les bibliothèques Swt et j'essaye de préparer le logiciel pour le déploiement. Comme il se avère SWT est très dépendante de la plate-forme/architecture. Je voudrais pouvoir emballer tous les six jar s (linux, mac, win et 32/64-bit) dans le même paquet et utiliser la bibliothèque appropriée selon le système. Je me rends compte que c'est un défi difficile cependant, passer à Swing (ou toute autre chose) n'est pas vraiment une option en ce moment.Problèmes de chargement des ressources lors de l'exécution

J'ai trouvé un certain nombre de sujets pertinents (@Aaron Digulla's thread et @mchr's thread) qui m'ont fourni des informations précieuses sur le problème rencontré. J'ai essayé de mettre en œuvre la solution proposée par @Alexey Romanov here. Avec une différence, comme la méthode loadSwtJar() qu'il propose n'est pas statique, j'instancie l'objet, et immédiatement après cela, exécutez la méthode avant toute autre chose à l'objet.

Il apparaît que la procédure de chargement ne fonctionne pas correctement. Mon raisonnement pour cette déclaration est la suivante:

  • Si tous Swt pots sont retirés de la classpath du fichier jar exécutable, puis Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/events/MouseListener est jeté qui est causée par: java.lang.ClassNotFoundException: org.eclipse.swt.events.MouseListener

pour moi, cela signifie que les bibliothèques ne sont pas trouvées sur classpath, je me trompe?

  • Si swt jars sont laissés sur le chemin de classe, le premier fichier jar est utilisé par le système pendant l'exécution. Cela signifie que si gtk-linux-x86_64 se trouve être le premier pot swt sur la liste des pots, alors le système essaye de l'utiliser, peu importe si le système est win32 ou Mac OSX.

J'ai essayé d'ajouter une sortie pour voir si la méthode loadSwtJar() est de choisir le pot droit, et la sortie semble à droite sur toutes les plates-formes que je l'ai essayé, comme dans le bon paquet est sélectionné (et les fichiers existent-ils dans le pot runnable). Mais la bonne bibliothèque n'est pas chargée donc des erreurs d'exécution se produisent: Exception in thread "main" java.lang.reflect.InvocationTargetException causé par par ex: Caused by: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM (Notez que c'est l'erreur que je reçois sur ma machine Linux si je change l'ordre d'apparition de 64 bits et 32 ​​bits swt bibliothèques sur le fichier build.xml)

Alors, quel semble être le problème ici? Ai-je manqué certains détails, ou est-il simplement impossible de vérifier les propriétés du système et de charger une bibliothèque appropriée en conséquence?

Enfin ci-dessous est un extrait de mon fichier de construction, pensé qu'il pourrait aider à trouver la source du problème.

Merci à l'avance,


EDIT: Après une longue session de débogage avec un collègue, le problème est résolu (à l'exception d'un bug gênant en ce qui concerne la gestion des threads sur MacOS comme je l'ai mentionné here). Il s'agissait de peaufiner avec la construction ANT ainsi que la façon dont la classe principale a été écrite.(La classe principale, en fin de compte, étendait les références de la bibliothèque SWT ce qui signifiait que le code ne compilerait pas du tout, encapsulerait la classe principale avec une autre classe et chargerait les jarres SWT qui semblaient être assez pour résoudre le problème)

Merci à tous ceux qui ont contribué, en particulier @Aaron. Vraiment apprécié!

Répondre

5

Voici une copie de la dernière version de ma classe principale. Laissez-moi savoir si cela fonctionne pour vous. Je l'ai testé sur Linux (32/64bit) et Windows (32bit).

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

Merci Aaron. Je dois dire que je ne suis pas très sûr de la façon dont vous creusez pour trouver les swtFiles, avez-vous vos fichiers swt dans un grand pot avec toutes les autres dépendances ou d'une autre manière? – posdef

+0

@Aaron: une autre question, avez-vous rencontré des problèmes avec les paquets SWT non signés? ou est-ce juste moi ... – posdef

+0

@posdef: J'ai mis tous les fichiers JAR normaux dans un répertoire appelé 'lib /' et les six fichiers JAR SWT dans 'swt /'. Si vous les mettez tous dans un répertoire, il est probable que le chemin de classe du manifeste du JAR principal tentera de charger le mauvais. –

0

Vous pouvez utiliser Java Web Start comme mécanisme d'amorçage pour votre application SWT multi-plateforme. Voir un corresponding entry dans la FAQ SWT. Vous pouvez également placer des bibliothèques natives SWT pour chaque plate-forme dans des dossiers distincts et les spécifier dans le script de démarrage spécifique à votre plate-forme. -Djava.library.path.

+0

J'ai vu l'entrée FAQ SWT, et j'espérais éviter cela, en d'autres termes avoir 6 fichiers différents à gérer. Mais je suppose que ça va être le plan B si ça ne marche pas à la fin. Au fait; Juste pour clarifier, la FAQ suggère de séparer les bibliothèques SWT du reste des dépendances et de les inclure au moment de l'exécution par le fichier JNLP, n'est-ce pas? – posdef

+0

Correct, le chargeur JNLP prendra soin de charger les libs et les pots spécifiques à la plate-forme pour vous. –

+0

J'ai regardé plus en détail cet exemple en tant que plan de sauvegarde, et je dois dire qu'il est plutôt suspect qu'ils n'aient pas été inclus dans l'exemple (même SunOS est inclus). De plus, existe-t-il une liste complète des valeurs de retour possibles pour les appels ''? – posdef