Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with loading resources during execution

Here's the background of the underlying problem, I am collaborating with a group on a project which uses Swt libraries and I am trying to package the software for deployment. As it turns out SWT is very platform/architecture dependent. I would like to be able to package all six jars (linux, mac, win and 32/64-bit) into the same package and use the appropriate library depending on the system. I realize that it is a tough challenge however, switching to Swing (or anything else) isn't really an option right now.

I have found a number of relevant threads (@Aaron Digulla's thread and @mchr's thread) which provided me valuable insights regarding the problem at hand. I have tried to implement the solution proposed by @Alexey Romanov here. With one difference, as the loadSwtJar() method he proposes is not static, I instantiate the object, and immediately following that, run the method before anything else is done to the object.

It appears as the loading procedure doesn't work properly. My reasoning for this statement is as follows:

  • If all Swt jars are removed from the classpath of the executable jar file, then Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/events/MouseListener is thrown which is caused by: java.lang.ClassNotFoundException: org.eclipse.swt.events.MouseListener

to me this means that the libraries are not found on classpath, am I mistaken?

  • If swt jars are left on the classpath then the first jar file is used by the system during execution. Meaning if gtk-linux-x86_64 happens to be the first swt jar on the list of jars then the system tries to use that, regardless if the system is win32 or Mac OSX.

I have tried to add some output to see if the loadSwtJar() method is choosing the right jar, and the output seems right on all platforms I have tried, as in the right package is selected (and the files do exist in the runnable jar). But nevertheless the right library is not loaded hence execution errors occur: Exception in thread "main" java.lang.reflect.InvocationTargetException caused by for ex: Caused by: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM (Note that this is the error I get on my Linux machine if I change the order of appearance of 64-bit and 32 bit swt libraries on the build.xml file)

So, what seems to be the problem here? Am I missing out on some detail, or is it simply not possible to check system properties and load an appropriate library accordingly?

Finally below is an excerpt of my build file, figured it might help finding the source of the problem.

Thanks in advance,


EDIT: After a long debug session with a colleague, the problem is resolved (except an annoying bug regarding Thread management on MacOS as I mentioned here). It involved tweaking with the ANT build as well as the way the main class was written. (The main class, as it turns out, was extending & implementing references from the SWT library which meant that the code wouldn't compile at all, wrapped the main class with another class and loaded the SWT jars from there which seemed to be enough to tackle the problem)

Thanks and regards to everyone who contributed, especially @Aaron. Really appreciated!

like image 931
posdef Avatar asked Oct 18 '10 13:10

posdef


1 Answers

Here is a copy of the latest version of my Main class. Let me know if that works for you. I tested it on Linux (32/64bit) and 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);
        }
    }
}
like image 158
Aaron Digulla Avatar answered Sep 25 '22 03:09

Aaron Digulla